summaryrefslogtreecommitdiffstats
path: root/src/qt3support
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt3support')
-rw-r--r--src/qt3support/canvas/canvas.pri2
-rw-r--r--src/qt3support/canvas/q3canvas.cpp5165
-rw-r--r--src/qt3support/canvas/q3canvas.h787
-rw-r--r--src/qt3support/dialogs/dialogs.pri16
-rw-r--r--src/qt3support/dialogs/q3filedialog.cpp6067
-rw-r--r--src/qt3support/dialogs/q3filedialog.h346
-rw-r--r--src/qt3support/dialogs/q3filedialog_mac.cpp594
-rw-r--r--src/qt3support/dialogs/q3filedialog_win.cpp513
-rw-r--r--src/qt3support/dialogs/q3progressdialog.cpp850
-rw-r--r--src/qt3support/dialogs/q3progressdialog.h149
-rw-r--r--src/qt3support/dialogs/q3tabdialog.cpp1087
-rw-r--r--src/qt3support/dialogs/q3tabdialog.h143
-rw-r--r--src/qt3support/dialogs/q3wizard.cpp906
-rw-r--r--src/qt3support/dialogs/q3wizard.h141
-rw-r--r--src/qt3support/itemviews/itemviews.pri11
-rw-r--r--src/qt3support/itemviews/q3iconview.cpp6210
-rw-r--r--src/qt3support/itemviews/q3iconview.h519
-rw-r--r--src/qt3support/itemviews/q3listbox.cpp4687
-rw-r--r--src/qt3support/itemviews/q3listbox.h429
-rw-r--r--src/qt3support/itemviews/q3listview.cpp7953
-rw-r--r--src/qt3support/itemviews/q3listview.h609
-rw-r--r--src/qt3support/itemviews/q3table.cpp7334
-rw-r--r--src/qt3support/itemviews/q3table.h548
-rw-r--r--src/qt3support/network/network.pri30
-rw-r--r--src/qt3support/network/q3dns.cpp2598
-rw-r--r--src/qt3support/network/q3dns.h174
-rw-r--r--src/qt3support/network/q3ftp.cpp2378
-rw-r--r--src/qt3support/network/q3ftp.h204
-rw-r--r--src/qt3support/network/q3http.cpp2321
-rw-r--r--src/qt3support/network/q3http.h277
-rw-r--r--src/qt3support/network/q3localfs.cpp404
-rw-r--r--src/qt3support/network/q3localfs.h84
-rw-r--r--src/qt3support/network/q3network.cpp73
-rw-r--r--src/qt3support/network/q3network.h63
-rw-r--r--src/qt3support/network/q3networkprotocol.cpp1209
-rw-r--r--src/qt3support/network/q3networkprotocol.h250
-rw-r--r--src/qt3support/network/q3serversocket.cpp298
-rw-r--r--src/qt3support/network/q3serversocket.h94
-rw-r--r--src/qt3support/network/q3socket.cpp1518
-rw-r--r--src/qt3support/network/q3socket.h157
-rw-r--r--src/qt3support/network/q3socketdevice.cpp757
-rw-r--r--src/qt3support/network/q3socketdevice.h177
-rw-r--r--src/qt3support/network/q3socketdevice_unix.cpp926
-rw-r--r--src/qt3support/network/q3socketdevice_win.cpp1062
-rw-r--r--src/qt3support/network/q3url.cpp1318
-rw-r--r--src/qt3support/network/q3url.h139
-rw-r--r--src/qt3support/network/q3urloperator.cpp1212
-rw-r--r--src/qt3support/network/q3urloperator.h138
-rw-r--r--src/qt3support/other/other.pri24
-rw-r--r--src/qt3support/other/q3accel.cpp982
-rw-r--r--src/qt3support/other/q3accel.h110
-rw-r--r--src/qt3support/other/q3boxlayout.cpp132
-rw-r--r--src/qt3support/other/q3boxlayout.h122
-rw-r--r--src/qt3support/other/q3dragobject.cpp1567
-rw-r--r--src/qt3support/other/q3dragobject.h218
-rw-r--r--src/qt3support/other/q3dropsite.cpp82
-rw-r--r--src/qt3support/other/q3dropsite.h65
-rw-r--r--src/qt3support/other/q3gridlayout.h78
-rw-r--r--src/qt3support/other/q3membuf.cpp171
-rw-r--r--src/qt3support/other/q3membuf_p.h103
-rw-r--r--src/qt3support/other/q3mimefactory.cpp546
-rw-r--r--src/qt3support/other/q3mimefactory.h102
-rw-r--r--src/qt3support/other/q3polygonscanner.cpp939
-rw-r--r--src/qt3support/other/q3polygonscanner.h70
-rw-r--r--src/qt3support/other/q3process.cpp927
-rw-r--r--src/qt3support/other/q3process.h186
-rw-r--r--src/qt3support/other/q3process_unix.cpp1283
-rw-r--r--src/qt3support/other/q3process_win.cpp628
-rw-r--r--src/qt3support/other/qiconset.h48
-rw-r--r--src/qt3support/other/qt_compat_pch.h66
-rw-r--r--src/qt3support/painting/painting.pri15
-rw-r--r--src/qt3support/painting/q3paintdevicemetrics.cpp149
-rw-r--r--src/qt3support/painting/q3paintdevicemetrics.h77
-rw-r--r--src/qt3support/painting/q3paintengine_svg.cpp1538
-rw-r--r--src/qt3support/painting/q3paintengine_svg_p.h128
-rw-r--r--src/qt3support/painting/q3painter.cpp240
-rw-r--r--src/qt3support/painting/q3painter.h123
-rw-r--r--src/qt3support/painting/q3picture.cpp235
-rw-r--r--src/qt3support/painting/q3picture.h68
-rw-r--r--src/qt3support/painting/q3pointarray.cpp189
-rw-r--r--src/qt3support/painting/q3pointarray.h74
-rw-r--r--src/qt3support/qt3support.pro39
-rw-r--r--src/qt3support/sql/q3databrowser.cpp1281
-rw-r--r--src/qt3support/sql/q3databrowser.h183
-rw-r--r--src/qt3support/sql/q3datatable.cpp2333
-rw-r--r--src/qt3support/sql/q3datatable.h251
-rw-r--r--src/qt3support/sql/q3dataview.cpp208
-rw-r--r--src/qt3support/sql/q3dataview.h90
-rw-r--r--src/qt3support/sql/q3editorfactory.cpp202
-rw-r--r--src/qt3support/sql/q3editorfactory.h77
-rw-r--r--src/qt3support/sql/q3sqlcursor.cpp1519
-rw-r--r--src/qt3support/sql/q3sqlcursor.h167
-rw-r--r--src/qt3support/sql/q3sqleditorfactory.cpp229
-rw-r--r--src/qt3support/sql/q3sqleditorfactory.h78
-rw-r--r--src/qt3support/sql/q3sqlfieldinfo.h167
-rw-r--r--src/qt3support/sql/q3sqlfieldinfo.qdoc220
-rw-r--r--src/qt3support/sql/q3sqlform.cpp378
-rw-r--r--src/qt3support/sql/q3sqlform.h109
-rw-r--r--src/qt3support/sql/q3sqlmanager_p.cpp961
-rw-r--r--src/qt3support/sql/q3sqlmanager_p.h160
-rw-r--r--src/qt3support/sql/q3sqlpropertymap.cpp276
-rw-r--r--src/qt3support/sql/q3sqlpropertymap.h86
-rw-r--r--src/qt3support/sql/q3sqlrecordinfo.h122
-rw-r--r--src/qt3support/sql/q3sqlrecordinfo.qdoc75
-rw-r--r--src/qt3support/sql/q3sqlselectcursor.cpp263
-rw-r--r--src/qt3support/sql/q3sqlselectcursor.h115
-rw-r--r--src/qt3support/sql/sql.pri25
-rw-r--r--src/qt3support/text/q3multilineedit.cpp535
-rw-r--r--src/qt3support/text/q3multilineedit.h143
-rw-r--r--src/qt3support/text/q3richtext.cpp8353
-rw-r--r--src/qt3support/text/q3richtext_p.cpp636
-rw-r--r--src/qt3support/text/q3richtext_p.h2102
-rw-r--r--src/qt3support/text/q3simplerichtext.cpp421
-rw-r--r--src/qt3support/text/q3simplerichtext.h109
-rw-r--r--src/qt3support/text/q3stylesheet.cpp1471
-rw-r--r--src/qt3support/text/q3stylesheet.h235
-rw-r--r--src/qt3support/text/q3syntaxhighlighter.cpp223
-rw-r--r--src/qt3support/text/q3syntaxhighlighter.h89
-rw-r--r--src/qt3support/text/q3syntaxhighlighter_p.h114
-rw-r--r--src/qt3support/text/q3textbrowser.cpp526
-rw-r--r--src/qt3support/text/q3textbrowser.h108
-rw-r--r--src/qt3support/text/q3textedit.cpp7244
-rw-r--r--src/qt3support/text/q3textedit.h613
-rw-r--r--src/qt3support/text/q3textstream.cpp2436
-rw-r--r--src/qt3support/text/q3textstream.h297
-rw-r--r--src/qt3support/text/q3textview.cpp84
-rw-r--r--src/qt3support/text/q3textview.h76
-rw-r--r--src/qt3support/text/text.pri25
-rw-r--r--src/qt3support/tools/q3asciicache.h132
-rw-r--r--src/qt3support/tools/q3asciicache.qdoc451
-rw-r--r--src/qt3support/tools/q3asciidict.h130
-rw-r--r--src/qt3support/tools/q3asciidict.qdoc402
-rw-r--r--src/qt3support/tools/q3cache.h130
-rw-r--r--src/qt3support/tools/q3cache.qdoc447
-rw-r--r--src/qt3support/tools/q3cleanuphandler.h110
-rw-r--r--src/qt3support/tools/q3cstring.cpp1072
-rw-r--r--src/qt3support/tools/q3cstring.h273
-rw-r--r--src/qt3support/tools/q3deepcopy.cpp123
-rw-r--r--src/qt3support/tools/q3deepcopy.h89
-rw-r--r--src/qt3support/tools/q3dict.h130
-rw-r--r--src/qt3support/tools/q3dict.qdoc432
-rw-r--r--src/qt3support/tools/q3garray.cpp798
-rw-r--r--src/qt3support/tools/q3garray.h140
-rw-r--r--src/qt3support/tools/q3gcache.cpp867
-rw-r--r--src/qt3support/tools/q3gcache.h137
-rw-r--r--src/qt3support/tools/q3gdict.cpp1154
-rw-r--r--src/qt3support/tools/q3gdict.h233
-rw-r--r--src/qt3support/tools/q3glist.cpp1270
-rw-r--r--src/qt3support/tools/q3glist.h279
-rw-r--r--src/qt3support/tools/q3gvector.cpp597
-rw-r--r--src/qt3support/tools/q3gvector.h132
-rw-r--r--src/qt3support/tools/q3intcache.h131
-rw-r--r--src/qt3support/tools/q3intcache.qdoc432
-rw-r--r--src/qt3support/tools/q3intdict.h126
-rw-r--r--src/qt3support/tools/q3intdict.qdoc376
-rw-r--r--src/qt3support/tools/q3memarray.h144
-rw-r--r--src/qt3support/tools/q3memarray.qdoc509
-rw-r--r--src/qt3support/tools/q3objectdict.h74
-rw-r--r--src/qt3support/tools/q3ptrcollection.cpp186
-rw-r--r--src/qt3support/tools/q3ptrcollection.h83
-rw-r--r--src/qt3support/tools/q3ptrdict.h127
-rw-r--r--src/qt3support/tools/q3ptrdict.qdoc374
-rw-r--r--src/qt3support/tools/q3ptrlist.h198
-rw-r--r--src/qt3support/tools/q3ptrlist.qdoc1144
-rw-r--r--src/qt3support/tools/q3ptrqueue.h99
-rw-r--r--src/qt3support/tools/q3ptrqueue.qdoc216
-rw-r--r--src/qt3support/tools/q3ptrstack.h99
-rw-r--r--src/qt3support/tools/q3ptrstack.qdoc203
-rw-r--r--src/qt3support/tools/q3ptrvector.h121
-rw-r--r--src/qt3support/tools/q3ptrvector.qdoc413
-rw-r--r--src/qt3support/tools/q3semaphore.cpp254
-rw-r--r--src/qt3support/tools/q3semaphore.h83
-rw-r--r--src/qt3support/tools/q3shared.cpp72
-rw-r--r--src/qt3support/tools/q3shared.h65
-rw-r--r--src/qt3support/tools/q3signal.cpp226
-rw-r--r--src/qt3support/tools/q3signal.h97
-rw-r--r--src/qt3support/tools/q3sortedlist.h71
-rw-r--r--src/qt3support/tools/q3strlist.h137
-rw-r--r--src/qt3support/tools/q3strvec.h93
-rw-r--r--src/qt3support/tools/q3tl.h212
-rw-r--r--src/qt3support/tools/q3valuelist.h238
-rw-r--r--src/qt3support/tools/q3valuelist.qdoc555
-rw-r--r--src/qt3support/tools/q3valuestack.h75
-rw-r--r--src/qt3support/tools/q3valuestack.qdoc135
-rw-r--r--src/qt3support/tools/q3valuevector.h113
-rw-r--r--src/qt3support/tools/q3valuevector.qdoc260
-rw-r--r--src/qt3support/tools/tools.pri44
-rw-r--r--src/qt3support/widgets/q3action.cpp2236
-rw-r--r--src/qt3support/widgets/q3action.h225
-rw-r--r--src/qt3support/widgets/q3button.cpp127
-rw-r--r--src/qt3support/widgets/q3button.h71
-rw-r--r--src/qt3support/widgets/q3buttongroup.cpp565
-rw-r--r--src/qt3support/widgets/q3buttongroup.h152
-rw-r--r--src/qt3support/widgets/q3combobox.cpp2357
-rw-r--r--src/qt3support/widgets/q3combobox.h224
-rw-r--r--src/qt3support/widgets/q3datetimeedit.cpp2807
-rw-r--r--src/qt3support/widgets/q3datetimeedit.h288
-rw-r--r--src/qt3support/widgets/q3dockarea.cpp1351
-rw-r--r--src/qt3support/widgets/q3dockarea.h199
-rw-r--r--src/qt3support/widgets/q3dockwindow.cpp2115
-rw-r--r--src/qt3support/widgets/q3dockwindow.h239
-rw-r--r--src/qt3support/widgets/q3frame.cpp200
-rw-r--r--src/qt3support/widgets/q3frame.h90
-rw-r--r--src/qt3support/widgets/q3grid.cpp138
-rw-r--r--src/qt3support/widgets/q3grid.h79
-rw-r--r--src/qt3support/widgets/q3gridview.cpp367
-rw-r--r--src/qt3support/widgets/q3gridview.h137
-rw-r--r--src/qt3support/widgets/q3groupbox.cpp964
-rw-r--r--src/qt3support/widgets/q3groupbox.h159
-rw-r--r--src/qt3support/widgets/q3hbox.cpp145
-rw-r--r--src/qt3support/widgets/q3hbox.h77
-rw-r--r--src/qt3support/widgets/q3header.cpp2040
-rw-r--r--src/qt3support/widgets/q3header.h225
-rw-r--r--src/qt3support/widgets/q3hgroupbox.cpp92
-rw-r--r--src/qt3support/widgets/q3hgroupbox.h69
-rw-r--r--src/qt3support/widgets/q3mainwindow.cpp2427
-rw-r--r--src/qt3support/widgets/q3mainwindow.h267
-rw-r--r--src/qt3support/widgets/q3mainwindow_p.h116
-rw-r--r--src/qt3support/widgets/q3popupmenu.cpp190
-rw-r--r--src/qt3support/widgets/q3popupmenu.h93
-rw-r--r--src/qt3support/widgets/q3progressbar.cpp464
-rw-r--r--src/qt3support/widgets/q3progressbar.h148
-rw-r--r--src/qt3support/widgets/q3rangecontrol.cpp550
-rw-r--r--src/qt3support/widgets/q3rangecontrol.h194
-rw-r--r--src/qt3support/widgets/q3scrollview.cpp2807
-rw-r--r--src/qt3support/widgets/q3scrollview.h253
-rw-r--r--src/qt3support/widgets/q3spinwidget.cpp475
-rw-r--r--src/qt3support/widgets/q3titlebar.cpp630
-rw-r--r--src/qt3support/widgets/q3titlebar_p.h134
-rw-r--r--src/qt3support/widgets/q3toolbar.cpp840
-rw-r--r--src/qt3support/widgets/q3toolbar.h122
-rw-r--r--src/qt3support/widgets/q3vbox.cpp72
-rw-r--r--src/qt3support/widgets/q3vbox.h67
-rw-r--r--src/qt3support/widgets/q3vgroupbox.cpp92
-rw-r--r--src/qt3support/widgets/q3vgroupbox.h69
-rw-r--r--src/qt3support/widgets/q3whatsthis.cpp220
-rw-r--r--src/qt3support/widgets/q3whatsthis.h89
-rw-r--r--src/qt3support/widgets/q3widgetstack.cpp571
-rw-r--r--src/qt3support/widgets/q3widgetstack.h112
-rw-r--r--src/qt3support/widgets/widgets.pri58
240 files changed, 155777 insertions, 0 deletions
diff --git a/src/qt3support/canvas/canvas.pri b/src/qt3support/canvas/canvas.pri
new file mode 100644
index 0000000..22d872d
--- /dev/null
+++ b/src/qt3support/canvas/canvas.pri
@@ -0,0 +1,2 @@
+HEADERS += canvas/q3canvas.h
+SOURCES += canvas/q3canvas.cpp
diff --git a/src/qt3support/canvas/q3canvas.cpp b/src/qt3support/canvas/q3canvas.cpp
new file mode 100644
index 0000000..74d07af
--- /dev/null
+++ b/src/qt3support/canvas/q3canvas.cpp
@@ -0,0 +1,5165 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3canvas.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdesktopwidget.h"
+#include "qimage.h"
+#include "q3ptrdict.h"
+#include "qpainter.h"
+#include "q3polygonscanner.h"
+#include "qtimer.h"
+#include "q3tl.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+class Q3CanvasData {
+public:
+ Q3CanvasData() :
+ itemDict(1013), animDict(503)
+ {
+ }
+
+ Q3PtrList<Q3CanvasView> viewList;
+ Q3PtrDict<void> itemDict;
+ Q3PtrDict<void> animDict;
+};
+
+class Q3CanvasViewData {
+public:
+ Q3CanvasViewData() {}
+#ifndef QT_NO_TRANSFORMATIONS
+ QMatrix xform;
+ QMatrix ixform;
+#endif
+ QRegion eraseRegion;
+};
+
+// clusterizer
+
+class Q3CanvasClusterizer {
+public:
+ Q3CanvasClusterizer(int maxclusters);
+ ~Q3CanvasClusterizer();
+
+ void add(int x, int y); // 1x1 rectangle (point)
+ void add(int x, int y, int w, int h);
+ void add(const QRect& rect);
+
+ void clear();
+ int clusters() const { return count; }
+ const QRect& operator[](int i) const;
+
+private:
+ QRect* cluster;
+ int count;
+ const int maxcl;
+};
+
+static
+void include(QRect& r, const QRect& rect)
+{
+ if (rect.left()<r.left()) {
+ r.setLeft(rect.left());
+ }
+ if (rect.right()>r.right()) {
+ r.setRight(rect.right());
+ }
+ if (rect.top()<r.top()) {
+ r.setTop(rect.top());
+ }
+ if (rect.bottom()>r.bottom()) {
+ r.setBottom(rect.bottom());
+ }
+}
+
+/*
+A Q3CanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles
+by a merging heuristic.
+*/
+Q3CanvasClusterizer::Q3CanvasClusterizer(int maxclusters) :
+ cluster(new QRect[maxclusters]),
+ count(0),
+ maxcl(maxclusters)
+{ }
+
+Q3CanvasClusterizer::~Q3CanvasClusterizer()
+{
+ delete [] cluster;
+}
+
+void Q3CanvasClusterizer::clear()
+{
+ count=0;
+}
+
+void Q3CanvasClusterizer::add(int x, int y)
+{
+ add(QRect(x,y,1,1));
+}
+
+void Q3CanvasClusterizer::add(int x, int y, int w, int h)
+{
+ add(QRect(x,y,w,h));
+}
+
+void Q3CanvasClusterizer::add(const QRect& rect)
+{
+ QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2);
+
+ //Q_ASSERT(rect.width()>0 && rect.height()>0);
+
+ int cursor;
+
+ for (cursor=0; cursor<count; cursor++) {
+ if (cluster[cursor].contains(rect)) {
+ // Wholly contained already.
+ return;
+ }
+ }
+
+ int lowestcost=9999999;
+ int cheapest=-1;
+ cursor = 0;
+ while(cursor<count) {
+ if (cluster[cursor].intersects(biggerrect)) {
+ QRect larger=cluster[cursor];
+ include(larger,rect);
+ int cost = larger.width()*larger.height() -
+ cluster[cursor].width()*cluster[cursor].height();
+
+ if (cost < lowestcost) {
+ bool bad=false;
+ for (int c=0; c<count && !bad; c++) {
+ bad=cluster[c].intersects(larger) && c!=cursor;
+ }
+ if (!bad) {
+ cheapest=cursor;
+ lowestcost=cost;
+ }
+ }
+ }
+ cursor++;
+ }
+
+ if (cheapest>=0) {
+ include(cluster[cheapest],rect);
+ return;
+ }
+
+ if (count < maxcl) {
+ cluster[count++]=rect;
+ return;
+ }
+
+ // Do cheapest of:
+ // add to closest cluster
+ // do cheapest cluster merge, add to new cluster
+
+ lowestcost=9999999;
+ cheapest=-1;
+ cursor=0;
+ while(cursor<count) {
+ QRect larger=cluster[cursor];
+ include(larger,rect);
+ int cost=larger.width()*larger.height()
+ - cluster[cursor].width()*cluster[cursor].height();
+ if (cost < lowestcost) {
+ bool bad=false;
+ for (int c=0; c<count && !bad; c++) {
+ bad=cluster[c].intersects(larger) && c!=cursor;
+ }
+ if (!bad) {
+ cheapest=cursor;
+ lowestcost=cost;
+ }
+ }
+ cursor++;
+ }
+
+ // ###
+ // could make an heuristic guess as to whether we need to bother
+ // looking for a cheap merge.
+
+ int cheapestmerge1 = -1;
+ int cheapestmerge2 = -1;
+
+ int merge1 = 0;
+ while(merge1 < count) {
+ int merge2=0;
+ while(merge2 < count) {
+ if(merge1!=merge2) {
+ QRect larger=cluster[merge1];
+ include(larger,cluster[merge2]);
+ int cost=larger.width()*larger.height()
+ - cluster[merge1].width()*cluster[merge1].height()
+ - cluster[merge2].width()*cluster[merge2].height();
+ if (cost < lowestcost) {
+ bool bad=false;
+ for (int c=0; c<count && !bad; c++) {
+ bad=cluster[c].intersects(larger) && c!=cursor;
+ }
+ if (!bad) {
+ cheapestmerge1=merge1;
+ cheapestmerge2=merge2;
+ lowestcost=cost;
+ }
+ }
+ }
+ merge2++;
+ }
+ merge1++;
+ }
+
+ if (cheapestmerge1>=0) {
+ include(cluster[cheapestmerge1],cluster[cheapestmerge2]);
+ cluster[cheapestmerge2]=cluster[count--];
+ } else {
+ // if (!cheapest) debugRectangles(rect);
+ include(cluster[cheapest],rect);
+ }
+
+ // NB: clusters do not intersect (or intersection will
+ // overwrite). This is a result of the above algorithm,
+ // given the assumption that (x,y) are ordered topleft
+ // to bottomright.
+
+ // ###
+ //
+ // add explicit x/y ordering to that comment, move it to the top
+ // and rephrase it as pre-/post-conditions.
+}
+
+const QRect& Q3CanvasClusterizer::operator[](int i) const
+{
+ return cluster[i];
+}
+
+// end of clusterizer
+
+// there's no more device coordinate clipping done, so introduce these
+// clip setting compat functions
+
+static void qt_setclipregion(QPainter *p, const QRegion &r)
+{
+ QMatrix matrix = p->worldMatrix();
+ p->setWorldMatrix(QMatrix());
+ p->setClipRegion(r);
+ p->setWorldMatrix(matrix);
+}
+
+static void qt_setcliprect(QPainter *p, const QRect &r)
+{
+ qt_setclipregion(p, QRegion(r));
+}
+
+
+class Q_COMPAT_EXPORT Q3CanvasItemPtr {
+public:
+ Q3CanvasItemPtr() : ptr(0) { }
+ Q3CanvasItemPtr(Q3CanvasItem* p) : ptr(p) { }
+
+ bool operator<=(const Q3CanvasItemPtr& that) const
+ {
+ // Order same-z objects by identity.
+ if (that.ptr->z()==ptr->z())
+ return that.ptr <= ptr;
+ return that.ptr->z() <= ptr->z();
+ }
+ bool operator<(const Q3CanvasItemPtr& that) const
+ {
+ // Order same-z objects by identity.
+ if (that.ptr->z()==ptr->z())
+ return that.ptr < ptr;
+ return that.ptr->z() < ptr->z();
+ }
+ bool operator>(const Q3CanvasItemPtr& that) const
+ {
+ // Order same-z objects by identity.
+ if (that.ptr->z()==ptr->z())
+ return that.ptr > ptr;
+ return that.ptr->z() > ptr->z();
+ }
+ bool operator==(const Q3CanvasItemPtr& that) const
+ {
+ return that.ptr == ptr;
+ }
+ operator Q3CanvasItem*() const { return ptr; }
+
+private:
+ Q3CanvasItem* ptr;
+};
+
+
+/*!
+ \class Q3CanvasItemList
+ \compat
+ \brief The Q3CanvasItemList class is a list of Q3CanvasItems.
+
+ Q3CanvasItemList is a Q3ValueList of pointers to \l{Q3CanvasItem}s.
+ This class is used by some methods in Q3Canvas that need to return
+ a list of canvas items.
+
+ The \l Q3ValueList documentation describes how to use this list.
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ \internal
+*/
+void Q3CanvasItemList::sort()
+{
+ qHeapSort(*((Q3ValueList<Q3CanvasItemPtr>*)this));
+}
+
+/*!
+ \internal
+*/
+void Q3CanvasItemList::drawUnique(QPainter& painter)
+{
+ Q3CanvasItem* prev=0;
+ for (Iterator it=fromLast(); it!=end(); --it) {
+ Q3CanvasItem *g=*it;
+ if (g!=prev) {
+ g->draw(painter);
+ prev=g;
+ }
+ }
+}
+
+/*!
+ Returns the concatenation of this list and list \a l.
+*/
+Q3CanvasItemList Q3CanvasItemList::operator+(const Q3CanvasItemList &l) const
+{
+ Q3CanvasItemList l2(*this);
+ for(const_iterator it = l.begin(); it != l.end(); ++it)
+ l2.append(*it);
+ return l2;
+}
+
+class Q3CanvasChunk {
+public:
+ Q3CanvasChunk() : changed(true) { }
+ // Other code assumes lists are not deleted. Assignment is also
+ // done on ChunkRecs. So don't add that sort of thing here.
+
+ void sort()
+ {
+ list.sort();
+ }
+
+ const Q3CanvasItemList* listPtr() const
+ {
+ return &list;
+ }
+
+ void add(Q3CanvasItem* item)
+ {
+ list.prepend(item);
+ changed = true;
+ }
+
+ void remove(Q3CanvasItem* item)
+ {
+ list.remove(item);
+ changed = true;
+ }
+
+ void change()
+ {
+ changed = true;
+ }
+
+ bool hasChanged() const
+ {
+ return changed;
+ }
+
+ bool takeChange()
+ {
+ bool y = changed;
+ changed = false;
+ return y;
+ }
+
+private:
+ Q3CanvasItemList list;
+ bool changed;
+};
+
+
+static int gcd(int a, int b)
+{
+ int r;
+ while ((r = a%b)) {
+ a=b;
+ b=r;
+ }
+ return b;
+}
+
+static int scm(int a, int b)
+{
+ int g = gcd(a,b);
+ return a/g*b;
+}
+
+
+
+/*!
+ \class Q3Canvas
+ \compat
+ \brief The Q3Canvas class provides a 2D area that can contain Q3CanvasItem objects.
+
+ The Q3Canvas class manages its 2D graphic area and all the canvas
+ items the area contains. The canvas has no visual appearance of
+ its own. Instead, it is displayed on screen using a Q3CanvasView.
+ Multiple Q3CanvasView widgets may be associated with a canvas to
+ provide multiple views of the same canvas.
+
+ The canvas is optimized for large numbers of items, particularly
+ where only a small percentage of the items change at any
+ one time. If the entire display changes very frequently, you should
+ consider using your own custom Q3ScrollView subclass.
+
+ Qt provides a rich
+ set of canvas item classes, e.g. Q3CanvasEllipse, Q3CanvasLine,
+ Q3CanvasPolygon, Q3CanvasPolygonalItem, Q3CanvasRectangle, Q3CanvasSpline,
+ Q3CanvasSprite and Q3CanvasText. You can subclass to create your own
+ canvas items; Q3CanvasPolygonalItem is the most common base class used
+ for this purpose.
+
+ Items appear on the canvas after their \link Q3CanvasItem::show()
+ show()\endlink function has been called (or \link
+ Q3CanvasItem::setVisible() setVisible(true)\endlink), and \e after
+ update() has been called. The canvas only shows items that are
+ \link Q3CanvasItem::setVisible() visible\endlink, and then only if
+ \l update() is called. (By default the canvas is white and so are
+ canvas items, so if nothing appears try changing colors.)
+
+ If you created the canvas without passing a width and height to
+ the constructor you must also call resize().
+
+ Although a canvas may appear to be similar to a widget with child
+ widgets, there are several notable differences:
+
+ \list
+ \i Canvas items are usually much faster to manipulate and redraw than
+ child widgets, with the speed advantage becoming especially great when
+ there are \e many canvas items and non-rectangular items. In most
+ situations canvas items are also a lot more memory efficient than child
+ widgets.
+
+ \i It's easy to detect overlapping items (collision detection).
+
+ \i The canvas can be larger than a widget. A million-by-million canvas
+ is perfectly possible. At such a size a widget might be very
+ inefficient, and some window systems might not support it at all,
+ whereas Q3Canvas scales well. Even with a billion pixels and a million
+ items, finding a particular canvas item, detecting collisions, etc.,
+ is still fast (though the memory consumption may be prohibitive
+ at such extremes).
+
+ \i Two or more Q3CanvasView objects can view the same canvas.
+
+ \i An arbitrary transformation matrix can be set on each Q3CanvasView
+ which makes it easy to zoom, rotate or shear the viewed canvas.
+
+ \i Widgets provide a lot more functionality, such as input (QKeyEvent,
+ QMouseEvent etc.) and layout management (QGridLayout etc.).
+
+ \endlist
+
+ A canvas consists of a background, a number of canvas items organized by
+ x, y and z coordinates, and a foreground. A canvas item's z coordinate
+ can be treated as a layer number -- canvas items with a higher z
+ coordinate appear in front of canvas items with a lower z coordinate.
+
+ The background is white by default, but can be set to a different color
+ using setBackgroundColor(), or to a repeated pixmap using
+ setBackgroundPixmap() or to a mosaic of smaller pixmaps using
+ setTiles(). Individual tiles can be set with setTile(). There
+ are corresponding get functions, e.g. backgroundColor() and
+ backgroundPixmap().
+
+ Note that Q3Canvas does not inherit from QWidget, even though it has some
+ functions which provide the same functionality as those in QWidget. One
+ of these is setBackgroundPixmap(); some others are resize(), size(),
+ width() and height(). \l Q3CanvasView is the widget used to display a
+ canvas on the screen.
+
+ Canvas items are added to a canvas by constructing them and passing the
+ canvas to the canvas item's constructor. An item can be moved to a
+ different canvas using Q3CanvasItem::setCanvas().
+
+ Canvas items are movable (and in the case of Q3CanvasSprites, animated)
+ objects that inherit Q3CanvasItem. Each canvas item has a position on the
+ canvas (x, y coordinates) and a height (z coordinate), all of which are
+ held as floating-point numbers. Moving canvas items also have x and y
+ velocities. It's possible for a canvas item to be outside the canvas
+ (for example Q3CanvasItem::x() is greater than width()). When a canvas
+ item is off the canvas, onCanvas() returns false and the canvas
+ disregards the item. (Canvas items off the canvas do not slow down any
+ of the common operations on the canvas.)
+
+ Canvas items can be moved with Q3CanvasItem::move(). The advance()
+ function moves all Q3CanvasItem::animated() canvas items and
+ setAdvancePeriod() makes Q3Canvas move them automatically on a periodic
+ basis. In the context of the Q3Canvas classes, to `animate' a canvas item
+ is to set it in motion, i.e. using Q3CanvasItem::setVelocity(). Animation
+ of a canvas item itself, i.e. items which change over time, is enabled
+ by calling Q3CanvasSprite::setFrameAnimation(), or more generally by
+ subclassing and reimplementing Q3CanvasItem::advance(). To detect collisions
+ use one of the Q3CanvasItem::collisions() functions.
+
+ The changed parts of the canvas are redrawn (if they are visible in a
+ canvas view) whenever update() is called. You can either call update()
+ manually after having changed the contents of the canvas, or force
+ periodic updates using setUpdatePeriod(). If you have moving objects on
+ the canvas, you must call advance() every time the objects should
+ move one step further. Periodic calls to advance() can be forced using
+ setAdvancePeriod(). The advance() function will call
+ Q3CanvasItem::advance() on every item that is \link
+ Q3CanvasItem::animated() animated\endlink and trigger an update of the
+ affected areas afterwards. (A canvas item that is `animated' is simply
+ a canvas item that is in motion.)
+
+ Q3Canvas organizes its canvas items into \e chunks; these are areas on
+ the canvas that are used to speed up most operations. Many operations
+ start by eliminating most chunks (i.e. those which haven't changed)
+ and then process only the canvas items that are in the few interesting
+ (i.e. changed) chunks. A valid chunk, validChunk(), is one which is on
+ the canvas.
+
+ The chunk size is a key factor to Q3Canvas's speed: if there are too many
+ chunks, the speed benefit of grouping canvas items into chunks is
+ reduced. If the chunks are too large, it takes too long to process each
+ one. The Q3Canvas constructor tries to pick a suitable size, but you
+ can call retune() to change it at any time. The chunkSize() function
+ returns the current chunk size. The canvas items always make sure
+ they're in the right chunks; all you need to make sure of is that
+ the canvas uses the right chunk size. A good rule of thumb is that
+ the size should be a bit smaller than the average canvas item
+ size. If you have moving objects, the chunk size should be a bit
+ smaller than the average size of the moving items.
+
+ The foreground is normally nothing, but if you reimplement
+ drawForeground(), you can draw things in front of all the canvas
+ items.
+
+ Areas can be set as changed with setChanged() and set unchanged with
+ setUnchanged(). The entire canvas can be set as changed with
+ setAllChanged(). A list of all the items on the canvas is returned by
+ allItems().
+
+ An area can be copied (painted) to a QPainter with drawArea().
+
+ If the canvas is resized it emits the resized() signal.
+
+ The examples/canvas application and the 2D graphics page of the
+ examples/demo application demonstrate many of Q3Canvas's facilities.
+
+ \sa Q3CanvasView Q3CanvasItem, QtCanvas, {Porting to Graphics View}
+*/
+void Q3Canvas::init(int w, int h, int chunksze, int mxclusters)
+{
+ d = new Q3CanvasData;
+ awidth=w;
+ aheight=h;
+ chunksize=chunksze;
+ maxclusters=mxclusters;
+ chwidth=(w+chunksize-1)/chunksize;
+ chheight=(h+chunksize-1)/chunksize;
+ chunks=new Q3CanvasChunk[chwidth*chheight];
+ update_timer = 0;
+ bgcolor = white;
+ grid = 0;
+ htiles = 0;
+ vtiles = 0;
+ dblbuf = false;
+ debug_redraw_areas = false;
+}
+
+/*!
+ Create a Q3Canvas with no size. \a parent and \a name are passed to
+ the QObject superclass.
+
+ \warning You \e must call resize() at some time after creation to
+ be able to use the canvas.
+*/
+Q3Canvas::Q3Canvas(QObject* parent, const char* name)
+ : QObject(parent, name)
+{
+ init(0,0);
+}
+
+/*!
+ Constructs a Q3Canvas that is \a w pixels wide and \a h pixels high.
+*/
+Q3Canvas::Q3Canvas(int w, int h)
+{
+ init(w,h);
+}
+
+/*!
+ Constructs a Q3Canvas which will be composed of \a h tiles
+ horizontally and \a v tiles vertically. Each tile will be an image
+ \a tilewidth by \a tileheight pixels taken from pixmap \a p.
+
+ The pixmap \a p is a list of tiles, arranged left to right, (and
+ in the case of pixmaps that have multiple rows of tiles, top to
+ bottom), with tile 0 in the top-left corner, tile 1 next to the
+ right, and so on, e.g.
+
+ \table
+ \row \i 0 \i 1 \i 2 \i 3
+ \row \i 4 \i 5 \i 6 \i 7
+ \endtable
+
+ The Q3Canvas is initially sized to show exactly the given number of
+ tiles horizontally and vertically. If it is resized to be larger,
+ the entire matrix of tiles will be repeated as often as necessary
+ to cover the area. If it is smaller, tiles to the right and bottom
+ will not be visible.
+
+ \sa setTiles()
+*/
+Q3Canvas::Q3Canvas(QPixmap p,
+ int h, int v, int tilewidth, int tileheight)
+{
+ init(h*tilewidth, v*tileheight, scm(tilewidth,tileheight));
+ setTiles(p, h, v, tilewidth, tileheight);
+}
+
+void qt_unview(Q3Canvas* c)
+{
+ for (Q3CanvasView* view=c->d->viewList.first(); view != 0; view=c->d->viewList.next()) {
+ view->viewing = 0;
+ }
+}
+
+/*!
+ Destroys the canvas and all the canvas's canvas items.
+*/
+Q3Canvas::~Q3Canvas()
+{
+ qt_unview(this);
+ Q3CanvasItemList all = allItems();
+ for (Q3CanvasItemList::Iterator it=all.begin(); it!=all.end(); ++it)
+ delete *it;
+ delete [] chunks;
+ delete [] grid;
+ delete d;
+}
+
+/*!
+\internal
+Returns the chunk at a chunk position \a i, \a j.
+*/
+Q3CanvasChunk& Q3Canvas::chunk(int i, int j) const
+{
+ return chunks[i+chwidth*j];
+}
+
+/*!
+\internal
+Returns the chunk at a pixel position \a x, \a y.
+*/
+Q3CanvasChunk& Q3Canvas::chunkContaining(int x, int y) const
+{
+ return chunk(x/chunksize,y/chunksize);
+}
+
+/*!
+ Returns a list of all the items in the canvas.
+*/
+Q3CanvasItemList Q3Canvas::allItems()
+{
+ Q3CanvasItemList list;
+ for (Q3PtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
+ list.prepend((Q3CanvasItem*)it.currentKey());
+ }
+ return list;
+}
+
+
+/*!
+ Changes the size of the canvas to have a width of \a w and a
+ height of \a h. This is a slow operation.
+*/
+void Q3Canvas::resize(int w, int h)
+{
+ if (awidth==w && aheight==h)
+ return;
+
+ Q3CanvasItem* item;
+ Q3PtrList<Q3CanvasItem> hidden;
+ for (Q3PtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
+ if (((Q3CanvasItem*)it.currentKey())->isVisible()) {
+ ((Q3CanvasItem*)it.currentKey())->hide();
+ hidden.append(((Q3CanvasItem*)it.currentKey()));
+ }
+ }
+
+ int nchwidth=(w+chunksize-1)/chunksize;
+ int nchheight=(h+chunksize-1)/chunksize;
+
+ Q3CanvasChunk* newchunks = new Q3CanvasChunk[nchwidth*nchheight];
+
+ // Commit the new values.
+ //
+ awidth=w;
+ aheight=h;
+ chwidth=nchwidth;
+ chheight=nchheight;
+ delete [] chunks;
+ chunks=newchunks;
+
+ for (item=hidden.first(); item != 0; item=hidden.next()) {
+ item->show();
+ }
+
+ setAllChanged();
+
+ emit resized();
+}
+
+/*!
+ \fn void Q3Canvas::resized()
+
+ This signal is emitted whenever the canvas is resized. Each
+ Q3CanvasView connects to this signal to keep the scrollview's size
+ correct.
+*/
+
+/*!
+ Change the efficiency tuning parameters to \a mxclusters clusters,
+ each of size \a chunksze. This is a slow operation if there are
+ many objects on the canvas.
+
+ The canvas is divided into chunks which are rectangular areas \a
+ chunksze wide by \a chunksze high. Use a chunk size which is about
+ the average size of the canvas items. If you choose a chunk size
+ which is too small it will increase the amount of calculation
+ required when drawing since each change will affect many chunks.
+ If you choose a chunk size which is too large the amount of
+ drawing required will increase because for each change, a lot of
+ drawing will be required since there will be many (unchanged)
+ canvas items which are in the same chunk as the changed canvas
+ items.
+
+ Internally, a canvas uses a low-resolution "chunk matrix" to keep
+ track of all the items in the canvas. A 64x64 chunk matrix is the
+ default for a 1024x1024 pixel canvas, where each chunk collects
+ canvas items in a 16x16 pixel square. This default is also
+ affected by setTiles(). You can tune this default using this
+ function. For example if you have a very large canvas and want to
+ trade off speed for memory then you might set the chunk size to 32
+ or 64.
+
+ The \a mxclusters argument is the number of rectangular groups of
+ chunks that will be separately drawn. If the canvas has a large
+ number of small, dispersed items, this should be about that
+ number. Our testing suggests that a large number of clusters is
+ almost always best.
+
+*/
+void Q3Canvas::retune(int chunksze, int mxclusters)
+{
+ maxclusters=mxclusters;
+
+ if (chunksize!=chunksze) {
+ Q3PtrList<Q3CanvasItem> hidden;
+ for (Q3PtrDictIterator<void> it=d->itemDict; it.currentKey(); ++it) {
+ if (((Q3CanvasItem*)it.currentKey())->isVisible()) {
+ ((Q3CanvasItem*)it.currentKey())->hide();
+ hidden.append(((Q3CanvasItem*)it.currentKey()));
+ }
+ }
+
+ chunksize=chunksze;
+
+ int nchwidth=(awidth+chunksize-1)/chunksize;
+ int nchheight=(aheight+chunksize-1)/chunksize;
+
+ Q3CanvasChunk* newchunks = new Q3CanvasChunk[nchwidth*nchheight];
+
+ // Commit the new values.
+ //
+ chwidth=nchwidth;
+ chheight=nchheight;
+ delete [] chunks;
+ chunks=newchunks;
+
+ for (Q3CanvasItem* item=hidden.first(); item != 0; item=hidden.next()) {
+ item->show();
+ }
+ }
+}
+
+/*!
+ \fn int Q3Canvas::width() const
+
+ Returns the width of the canvas, in pixels.
+*/
+
+/*!
+ \fn int Q3Canvas::height() const
+
+ Returns the height of the canvas, in pixels.
+*/
+
+/*!
+ \fn QSize Q3Canvas::size() const
+
+ Returns the size of the canvas, in pixels.
+*/
+
+/*!
+ \fn QRect Q3Canvas::rect() const
+
+ Returns a rectangle the size of the canvas.
+*/
+
+
+/*!
+ \fn bool Q3Canvas::onCanvas(int x, int y) const
+
+ Returns true if the pixel position (\a x, \a y) is on the canvas;
+ otherwise returns false.
+
+ \sa validChunk()
+*/
+
+/*!
+ \fn bool Q3Canvas::onCanvas(const QPoint& p) const
+ \overload
+
+ Returns true if the pixel position \a p is on the canvas;
+ otherwise returns false.
+
+ \sa validChunk()
+*/
+
+/*!
+ \fn bool Q3Canvas::validChunk(int x, int y) const
+
+ Returns true if the chunk position (\a x, \a y) is on the canvas;
+ otherwise returns false.
+
+ \sa onCanvas()
+*/
+
+/*!
+ \fn bool Q3Canvas::validChunk(const QPoint& p) const
+ \overload
+
+ Returns true if the chunk position \a p is on the canvas; otherwise
+ returns false.
+
+ \sa onCanvas()
+*/
+
+/*!
+ \fn int Q3Canvas::chunkSize() const
+
+ Returns the chunk size of the canvas.
+
+ \sa retune()
+*/
+
+/*!
+\fn bool Q3Canvas::sameChunk(int x1, int y1, int x2, int y2) const
+\internal
+Tells if the points (\a x1, \a y1) and (\a x2, \a y2) are within the same chunk.
+*/
+
+/*!
+\internal
+This method adds an the item \a item to the list of Q3CanvasItem objects
+in the Q3Canvas. The Q3CanvasItem class calls this.
+*/
+void Q3Canvas::addItem(Q3CanvasItem* item)
+{
+ d->itemDict.insert((void*)item,(void*)1);
+}
+
+/*!
+\internal
+This method adds the item \a item to the list of Q3CanvasItem objects
+to be moved. The Q3CanvasItem class calls this.
+*/
+void Q3Canvas::addAnimation(Q3CanvasItem* item)
+{
+ d->animDict.insert((void*)item,(void*)1);
+}
+
+/*!
+\internal
+This method adds the item \a item to the list of Q3CanvasItem objects
+which are no longer to be moved. The Q3CanvasItem class calls this.
+*/
+void Q3Canvas::removeAnimation(Q3CanvasItem* item)
+{
+ d->animDict.remove((void*)item);
+}
+
+/*!
+\internal
+This method removes the item \a item from the list of Q3CanvasItem objects
+in this Q3Canvas. The Q3CanvasItem class calls this.
+*/
+void Q3Canvas::removeItem(Q3CanvasItem* item)
+{
+ d->itemDict.remove((void*)item);
+}
+
+/*!
+\internal
+This method adds the view \a view to the list of Q3CanvasView objects
+viewing this Q3Canvas. The Q3CanvasView class calls this.
+*/
+void Q3Canvas::addView(Q3CanvasView* view)
+{
+ d->viewList.append(view);
+ if (htiles>1 || vtiles>1 || pm.isNull())
+ view->viewport()->setBackgroundColor(backgroundColor());
+}
+
+/*!
+\internal
+This method removes the view \a view from the list of Q3CanvasView objects
+viewing this Q3Canvas. The Q3CanvasView class calls this.
+*/
+void Q3Canvas::removeView(Q3CanvasView* view)
+{
+ d->viewList.removeRef(view);
+}
+
+/*!
+ Sets the canvas to call advance() every \a ms milliseconds. Any
+ previous setting by setAdvancePeriod() or setUpdatePeriod() is
+ overridden.
+
+ If \a ms is less than 0 advancing will be stopped.
+*/
+void Q3Canvas::setAdvancePeriod(int ms)
+{
+ if (ms<0) {
+ if (update_timer)
+ update_timer->stop();
+ } else {
+ if (update_timer)
+ delete update_timer;
+ update_timer = new QTimer(this);
+ connect(update_timer,SIGNAL(timeout()),this,SLOT(advance()));
+ update_timer->start(ms);
+ }
+}
+
+/*!
+ Sets the canvas to call update() every \a ms milliseconds. Any
+ previous setting by setAdvancePeriod() or setUpdatePeriod() is
+ overridden.
+
+ If \a ms is less than 0 automatic updating will be stopped.
+*/
+void Q3Canvas::setUpdatePeriod(int ms)
+{
+ if (ms<0) {
+ if (update_timer)
+ update_timer->stop();
+ } else {
+ if (update_timer)
+ delete update_timer;
+ update_timer = new QTimer(this);
+ connect(update_timer,SIGNAL(timeout()),this,SLOT(update()));
+ update_timer->start(ms);
+ }
+}
+
+/*!
+ Moves all Q3CanvasItem::animated() canvas items on the canvas and
+ refreshes all changes to all views of the canvas. (An `animated'
+ item is an item that is in motion; see setVelocity().)
+
+ The advance takes place in two phases. In phase 0, the
+ Q3CanvasItem::advance() function of each Q3CanvasItem::animated()
+ canvas item is called with parameter 0. Then all these canvas
+ items are called again, with parameter 1. In phase 0, the canvas
+ items should not change position, merely examine other items on
+ the canvas for which special processing is required, such as
+ collisions between items. In phase 1, all canvas items should
+ change positions, ignoring any other items on the canvas. This
+ two-phase approach allows for considerations of "fairness",
+ although no Q3CanvasItem subclasses supplied with Qt do anything
+ interesting in phase 0.
+
+ The canvas can be configured to call this function periodically
+ with setAdvancePeriod().
+
+ \sa update()
+*/
+void Q3Canvas::advance()
+{
+ Q3PtrDictIterator<void> it=d->animDict;
+ while (it.current()) {
+ Q3CanvasItem* i = (Q3CanvasItem*)(void*)it.currentKey();
+ ++it;
+ if (i)
+ i->advance(0);
+ }
+ // we expect the dict contains the exact same items as in the
+ // first pass.
+ it.toFirst();
+ while (it.current()) {
+ Q3CanvasItem* i = (Q3CanvasItem*)(void*)it.currentKey();
+ ++it;
+ if (i)
+ i->advance(1);
+ }
+ update();
+}
+
+// Don't call this unless you know what you're doing.
+// p is in the content's co-ordinate example.
+/*!
+ \internal
+*/
+void Q3Canvas::drawViewArea(Q3CanvasView* view, QPainter* p, const QRect& vr, bool)
+{
+ QPoint tl = view->contentsToViewport(QPoint(0,0));
+
+#ifndef QT_NO_TRANSFORMATIONS
+ QMatrix wm = view->worldMatrix();
+ QMatrix iwm = wm.invert();
+ // ivr = covers all chunks in vr
+ QRect ivr = iwm.map(vr);
+ QMatrix twm;
+ twm.translate(tl.x(),tl.y());
+#else
+ QRect ivr = vr;
+#endif
+
+ QRect all(0,0,width(),height());
+
+ if (!all.contains(ivr)) {
+ // Need to clip with edge of canvas.
+
+#ifndef QT_NO_TRANSFORMATIONS
+ // For translation-only transformation, it is safe to include the right
+ // and bottom edges, but otherwise, these must be excluded since they
+ // are not precisely defined (different bresenham paths).
+ Q3PointArray a;
+ if (wm.m12()==0.0 && wm.m21()==0.0 && wm.m11() == 1.0 && wm.m22() == 1.0)
+ a = Q3PointArray(QRect(all.x(),all.y(),all.width()+1,all.height()+1));
+ else
+ a = Q3PointArray(all);
+
+ a = (wm*twm).map(a);
+#else
+ Q3PointArray a(QRect(all.x(),all.y(),all.width()+1,all.height()+1));
+#endif
+ if (view->viewport()->backgroundMode() == NoBackground) {
+ QRect cvr = vr; cvr.moveBy(tl.x(),tl.y());
+ qt_setclipregion(p, QRegion(cvr)-QRegion(a));
+ p->fillRect(vr,view->viewport()->palette()
+ .brush(QPalette::Active,QPalette::Window));
+ }
+ qt_setclipregion(p, a);
+ }
+
+ QRect r = vr; r.moveBy(tl.x(),tl.y()); // move to untransformed co-ords
+ if (!all.contains(ivr)) {
+ QRegion inside = p->clipRegion() & r;
+ //QRegion outside = p->clipRegion() - r;
+ //p->setClipRegion(outside);
+ //p->fillRect(outside.boundingRect(),red);
+ qt_setclipregion(p, inside);
+ } else {
+ qt_setcliprect(p, r);
+ }
+#ifndef QT_NO_TRANSFORMATIONS
+ p->setWorldMatrix(wm*twm);
+#else
+#endif
+ drawCanvasArea(ivr,p,false);
+}
+
+/*!
+ Repaints changed areas in all views of the canvas.
+
+ \sa advance()
+*/
+void Q3Canvas::update()
+{
+ // ##### fix QT_NO_TRANSFORMATIONS
+#ifndef QT_NO_TRANSFORMATIONS
+ Q3PtrList<QRect> doneareas;
+ doneareas.setAutoDelete(true);
+#endif
+
+ Q3PtrListIterator<Q3CanvasView> it(d->viewList);
+ Q3CanvasView* view;
+ while((view=it.current()) != 0) {
+ ++it;
+#ifndef QT_NO_TRANSFORMATIONS
+ QMatrix wm = view->worldMatrix();
+#endif
+ QRect area(view->contentsX(),view->contentsY(),
+ view->visibleWidth(),view->visibleHeight());
+ if (area.width()>0 && area.height()>0) {
+#ifndef QT_NO_TRANSFORMATIONS
+ // r = Visible area of the canvas where there are changes
+ QRect r = changeBounds(view->inverseWorldMatrix().map(area));
+ if (!r.isEmpty()) {
+ QRect tr = wm.map(r);
+ tr.moveBy(-view->contentsX(), -view->contentsY());
+ view->viewport()->update(tr);
+ doneareas.append(new QRect(r));
+ }
+#endif
+ }
+ }
+
+#ifndef QT_NO_TRANSFORMATIONS
+ for (QRect* r=doneareas.first(); r != 0; r=doneareas.next())
+ setUnchanged(*r);
+#endif
+}
+
+
+/*!
+ Marks the whole canvas as changed.
+ All views of the canvas will be entirely redrawn when
+ update() is called next.
+*/
+void Q3Canvas::setAllChanged()
+{
+ setChanged(QRect(0,0,width(),height()));
+}
+
+/*!
+ Marks \a area as changed. This \a area will be redrawn in all
+ views that are showing it when update() is called next.
+*/
+void Q3Canvas::setChanged(const QRect& area)
+{
+ QRect thearea = area.intersected(QRect(0, 0, width(), height()));
+
+ int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
+ int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
+ if (mx>chwidth)
+ mx=chwidth;
+ if (my>chheight)
+ my=chheight;
+
+ int x=thearea.x()/chunksize;
+ while(x<mx) {
+ int y = thearea.y()/chunksize;
+ while(y<my) {
+ chunk(x,y).change();
+ y++;
+ }
+ x++;
+ }
+}
+
+/*!
+ Marks \a area as \e unchanged. The area will \e not be redrawn in
+ the views for the next update(), unless it is marked or changed
+ again before the next call to update().
+*/
+void Q3Canvas::setUnchanged(const QRect& area)
+{
+ QRect thearea = area.intersected(QRect(0, 0, width(), height()));
+
+ int mx = (thearea.x()+thearea.width()+chunksize)/chunksize;
+ int my = (thearea.y()+thearea.height()+chunksize)/chunksize;
+ if (mx>chwidth)
+ mx=chwidth;
+ if (my>chheight)
+ my=chheight;
+
+ int x=thearea.x()/chunksize;
+ while(x<mx) {
+ int y = thearea.y()/chunksize;
+ while(y<my) {
+ chunk(x,y).takeChange();
+ y++;
+ }
+ x++;
+ }
+}
+
+
+/*!
+ \internal
+*/
+QRect Q3Canvas::changeBounds(const QRect& inarea)
+{
+ QRect area = inarea.intersected(QRect(0, 0, width(), height()));
+
+ int mx = (area.x()+area.width()+chunksize)/chunksize;
+ int my = (area.y()+area.height()+chunksize)/chunksize;
+ if (mx > chwidth)
+ mx=chwidth;
+ if (my > chheight)
+ my=chheight;
+
+ QRect result;
+
+ int x=area.x()/chunksize;
+ while(x<mx) {
+ int y=area.y()/chunksize;
+ while(y<my) {
+ Q3CanvasChunk& ch=chunk(x,y);
+ if (ch.hasChanged())
+ result |= QRect(x,y,1,1);
+ y++;
+ }
+ x++;
+ }
+
+ if (!result.isEmpty()) {
+ result.rLeft() *= chunksize;
+ result.rTop() *= chunksize;
+ result.rRight() *= chunksize;
+ result.rBottom() *= chunksize;
+ result.rRight() += chunksize;
+ result.rBottom() += chunksize;
+ }
+
+ return result;
+}
+
+void Q3Canvas::ensureOffScrSize(int osw, int osh)
+{
+ if (osw > offscr.width() || osh > offscr.height())
+ offscr.resize(QMAX(osw,offscr.width()),
+ QMAX(osh,offscr.height()));
+ else if (offscr.width() == 0 || offscr.height() == 0)
+ offscr.resize(QMAX(offscr.width(), 1),
+ QMAX(offscr.height(), 1));
+}
+
+/*!
+ Paints all canvas items that are in the area \a clip to \a
+ painter, using double-buffering if \a dbuf is true.
+
+ e.g. to print the canvas to a printer:
+ \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 0
+*/
+void Q3Canvas::drawArea(const QRect& clip, QPainter* painter, bool dbuf)
+{
+ if (painter)
+ drawCanvasArea(clip, painter, dbuf);
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qdebug.h>
+QT_END_INCLUDE_NAMESPACE
+
+/*!
+ \internal
+*/
+void Q3Canvas::drawCanvasArea(const QRect& inarea, QPainter* p, bool /*double_buffer*/)
+{
+ QRect area=inarea.intersected(QRect(0,0,width(),height()));
+
+ if (!p) return; // Nothing to do.
+
+ int lx=area.x()/chunksize;
+ int ly=area.y()/chunksize;
+ int mx=area.right()/chunksize;
+ int my=area.bottom()/chunksize;
+ if (mx>=chwidth)
+ mx=chwidth-1;
+ if (my>=chheight)
+ my=chheight-1;
+
+ Q3CanvasItemList allvisible;
+
+ // Stores the region within area that need to be drawn. It is relative
+ // to area.topLeft() (so as to keep within bounds of 16-bit XRegions)
+ QRegion rgn;
+
+ for (int x=lx; x<=mx; x++) {
+ for (int y=ly; y<=my; y++) {
+ // Only reset change if all views updating, and
+ // wholy within area. (conservative: ignore entire boundary)
+ //
+ // Disable this to help debugging.
+ //
+ if (!p) {
+ if (chunk(x,y).takeChange()) {
+ // ### should at least make bands
+ rgn |= QRegion(x*chunksize-area.x(),y*chunksize-area.y(),
+ chunksize,chunksize);
+ allvisible += *chunk(x,y).listPtr();
+ }
+ } else {
+ allvisible += *chunk(x,y).listPtr();
+ }
+ }
+ }
+ allvisible.sort();
+
+ drawBackground(*p,area);
+ allvisible.drawUnique(*p);
+ drawForeground(*p,area);
+}
+
+/*!
+\internal
+This method to informs the Q3Canvas that a given chunk is
+`dirty' and needs to be redrawn in the next Update.
+
+(\a x,\a y) is a chunk location.
+
+The sprite classes call this. Any new derived class of Q3CanvasItem
+must do so too. SetChangedChunkContaining can be used instead.
+*/
+void Q3Canvas::setChangedChunk(int x, int y)
+{
+ if (validChunk(x,y)) {
+ Q3CanvasChunk& ch=chunk(x,y);
+ ch.change();
+ }
+}
+
+/*!
+\internal
+This method to informs the Q3Canvas that the chunk containing a given
+pixel is `dirty' and needs to be redrawn in the next Update.
+
+(\a x,\a y) is a pixel location.
+
+The item classes call this. Any new derived class of Q3CanvasItem must
+do so too. SetChangedChunk can be used instead.
+*/
+void Q3Canvas::setChangedChunkContaining(int x, int y)
+{
+ if (x>=0 && x<width() && y>=0 && y<height()) {
+ Q3CanvasChunk& chunk=chunkContaining(x,y);
+ chunk.change();
+ }
+}
+
+/*!
+\internal
+This method adds the Q3CanvasItem \a g to the list of those which need to be
+drawn if the given chunk at location (\a x, \a y) is redrawn. Like
+SetChangedChunk and SetChangedChunkContaining, this method marks the
+chunk as `dirty'.
+*/
+void Q3Canvas::addItemToChunk(Q3CanvasItem* g, int x, int y)
+{
+ if (validChunk(x,y)) {
+ chunk(x,y).add(g);
+ }
+}
+
+/*!
+\internal
+This method removes the Q3CanvasItem \a g from the list of those which need to
+be drawn if the given chunk at location (\a x, \a y) is redrawn. Like
+SetChangedChunk and SetChangedChunkContaining, this method marks the chunk
+as `dirty'.
+*/
+void Q3Canvas::removeItemFromChunk(Q3CanvasItem* g, int x, int y)
+{
+ if (validChunk(x,y)) {
+ chunk(x,y).remove(g);
+ }
+}
+
+
+/*!
+\internal
+This method adds the Q3CanvasItem \a g to the list of those which need to be
+drawn if the chunk containing the given pixel (\a x, \a y) is redrawn. Like
+SetChangedChunk and SetChangedChunkContaining, this method marks the
+chunk as `dirty'.
+*/
+void Q3Canvas::addItemToChunkContaining(Q3CanvasItem* g, int x, int y)
+{
+ if (x>=0 && x<width() && y>=0 && y<height()) {
+ chunkContaining(x,y).add(g);
+ }
+}
+
+/*!
+\internal
+This method removes the Q3CanvasItem \a g from the list of those which need to
+be drawn if the chunk containing the given pixel (\a x, \a y) is redrawn.
+Like SetChangedChunk and SetChangedChunkContaining, this method
+marks the chunk as `dirty'.
+*/
+void Q3Canvas::removeItemFromChunkContaining(Q3CanvasItem* g, int x, int y)
+{
+ if (x>=0 && x<width() && y>=0 && y<height()) {
+ chunkContaining(x,y).remove(g);
+ }
+}
+
+/*!
+ Returns the color set by setBackgroundColor(). By default, this is
+ white.
+
+ This function is not a reimplementation of
+ QWidget::backgroundColor() (Q3Canvas is not a subclass of QWidget),
+ but all Q3CanvasViews that are viewing the canvas will set their
+ backgrounds to this color.
+
+ \sa setBackgroundColor(), backgroundPixmap()
+*/
+QColor Q3Canvas::backgroundColor() const
+{
+ return bgcolor;
+}
+
+/*!
+ Sets the solid background to be the color \a c.
+
+ \sa backgroundColor(), setBackgroundPixmap(), setTiles()
+*/
+void Q3Canvas::setBackgroundColor(const QColor& c)
+{
+ if (bgcolor != c) {
+ bgcolor = c;
+ Q3CanvasView* view=d->viewList.first();
+ while (view != 0) {
+ /* XXX this doesn't look right. Shouldn't this
+ be more like setBackgroundPixmap? : Ian */
+ view->viewport()->setEraseColor(bgcolor);
+ view=d->viewList.next();
+ }
+ setAllChanged();
+ }
+}
+
+/*!
+ Returns the pixmap set by setBackgroundPixmap(). By default,
+ this is a null pixmap.
+
+ \sa setBackgroundPixmap(), backgroundColor()
+*/
+QPixmap Q3Canvas::backgroundPixmap() const
+{
+ return pm;
+}
+
+/*!
+ Sets the solid background to be the pixmap \a p repeated as
+ necessary to cover the entire canvas.
+
+ \sa backgroundPixmap(), setBackgroundColor(), setTiles()
+*/
+void Q3Canvas::setBackgroundPixmap(const QPixmap& p)
+{
+ setTiles(p, 1, 1, p.width(), p.height());
+ Q3CanvasView* view = d->viewList.first();
+ while (view != 0) {
+ view->updateContents();
+ view = d->viewList.next();
+ }
+}
+
+/*!
+ This virtual function is called for all updates of the canvas. It
+ renders any background graphics using the painter \a painter, in
+ the area \a clip. If the canvas has a background pixmap or a tiled
+ background, that graphic is used, otherwise the canvas is cleared
+ using the background color.
+
+ If the graphics for an area change, you must explicitly call
+ setChanged(const QRect&) for the result to be visible when
+ update() is next called.
+
+ \sa setBackgroundColor(), setBackgroundPixmap(), setTiles()
+*/
+void Q3Canvas::drawBackground(QPainter& painter, const QRect& clip)
+{
+ if (pm.isNull()) {
+ painter.fillRect(clip,bgcolor);
+ } else if (!grid) {
+ for (int x=clip.x()/pm.width();
+ x<(clip.x()+clip.width()+pm.width()-1)/pm.width(); x++)
+ {
+ for (int y=clip.y()/pm.height();
+ y<(clip.y()+clip.height()+pm.height()-1)/pm.height(); y++)
+ {
+ painter.drawPixmap(x*pm.width(), y*pm.height(),pm);
+ }
+ }
+ } else {
+ const int x1 = clip.left()/tilew;
+ int x2 = clip.right()/tilew;
+ const int y1 = clip.top()/tileh;
+ int y2 = clip.bottom()/tileh;
+
+ const int roww = pm.width()/tilew;
+
+ for (int j=y1; j<=y2; j++) {
+ int jj = j%tilesVertically();
+ for (int i=x1; i<=x2; i++) {
+ int t = tile(i%tilesHorizontally(), jj);
+ int tx = t % roww;
+ int ty = t / roww;
+ painter.drawPixmap(i*tilew, j*tileh, pm,
+ tx*tilew, ty*tileh, tilew, tileh);
+ }
+ }
+ }
+}
+
+/*!
+ This virtual function is called for all updates of the canvas. It
+ renders any foreground graphics using the painter \a painter, in
+ the area \a clip.
+
+ If the graphics for an area change, you must explicitly call
+ setChanged(const QRect&) for the result to be visible when
+ update() is next called.
+
+ The default is to draw nothing.
+*/
+void Q3Canvas::drawForeground(QPainter& painter, const QRect& clip)
+{
+ if (debug_redraw_areas) {
+ painter.setPen(red);
+ painter.setBrush(NoBrush);
+ painter.drawRect(clip);
+ }
+}
+
+/*!
+ If \a y is true (the default) double-buffering is switched on;
+ otherwise double-buffering is switched off.
+
+ Turning off double-buffering causes the redrawn areas to flicker a
+ little and also gives a (usually small) performance improvement.
+*/
+void Q3Canvas::setDoubleBuffering(bool y)
+{
+ dblbuf = y;
+}
+
+
+/*!
+ Sets the Q3Canvas to be composed of \a h tiles horizontally and \a
+ v tiles vertically. Each tile will be an image \a tilewidth by \a
+ tileheight pixels from pixmap \a p.
+
+ The pixmap \a p is a list of tiles, arranged left to right, (and
+ in the case of pixmaps that have multiple rows of tiles, top to
+ bottom), with tile 0 in the top-left corner, tile 1 next to the
+ right, and so on, e.g.
+
+ \table
+ \row \i 0 \i 1 \i 2 \i 3
+ \row \i 4 \i 5 \i 6 \i 7
+ \endtable
+
+ If the canvas is larger than the matrix of tiles, the entire
+ matrix is repeated as necessary to cover the whole canvas. If it
+ is smaller, tiles to the right and bottom are not visible.
+
+ The width and height of \a p must be a multiple of \a tilewidth
+ and \a tileheight. If they are not the function will do nothing.
+
+ If you want to unset any tiling set, then just pass in a null
+ pixmap and 0 for \a h, \a v, \a tilewidth, and
+ \a tileheight.
+*/
+void Q3Canvas::setTiles(QPixmap p,
+ int h, int v, int tilewidth, int tileheight)
+{
+ if (!p.isNull() && (!tilewidth || !tileheight ||
+ p.width() % tilewidth != 0 || p.height() % tileheight != 0))
+ return;
+
+ htiles = h;
+ vtiles = v;
+ delete[] grid;
+ pm = p;
+ if (h && v && !p.isNull()) {
+ grid = new ushort[h*v];
+ memset(grid, 0, h*v*sizeof(ushort));
+ tilew = tilewidth;
+ tileh = tileheight;
+ } else {
+ grid = 0;
+ }
+ if (h + v > 10) {
+ int s = scm(tilewidth,tileheight);
+ retune(s < 128 ? s : QMAX(tilewidth,tileheight));
+ }
+ setAllChanged();
+}
+
+/*!
+ \fn int Q3Canvas::tile(int x, int y) const
+
+ Returns the tile at position (\a x, \a y). Initially, all tiles
+ are 0.
+
+ The parameters must be within range, i.e.
+ 0 \< \a x \< tilesHorizontally() and
+ 0 \< \a y \< tilesVertically().
+
+ \sa setTile()
+*/
+
+/*!
+ \fn int Q3Canvas::tilesHorizontally() const
+
+ Returns the number of tiles horizontally.
+*/
+
+/*!
+ \fn int Q3Canvas::tilesVertically() const
+
+ Returns the number of tiles vertically.
+*/
+
+/*!
+ \fn int Q3Canvas::tileWidth() const
+
+ Returns the width of each tile.
+*/
+
+/*!
+ \fn int Q3Canvas::tileHeight() const
+
+ Returns the height of each tile.
+*/
+
+
+/*!
+ Sets the tile at (\a x, \a y) to use tile number \a tilenum, which
+ is an index into the tile pixmaps. The canvas will update
+ appropriately when update() is next called.
+
+ The images are taken from the pixmap set by setTiles() and are
+ arranged left to right, (and in the case of pixmaps that have
+ multiple rows of tiles, top to bottom), with tile 0 in the
+ top-left corner, tile 1 next to the right, and so on, e.g.
+
+ \table
+ \row \i 0 \i 1 \i 2 \i 3
+ \row \i 4 \i 5 \i 6 \i 7
+ \endtable
+
+ \sa tile() setTiles()
+*/
+void Q3Canvas::setTile(int x, int y, int tilenum)
+{
+ ushort& t = grid[x+y*htiles];
+ if (t != tilenum) {
+ t = tilenum;
+ if (tilew == tileh && tilew == chunksize)
+ setChangedChunk(x, y); // common case
+ else
+ setChanged(QRect(x*tilew,y*tileh,tilew,tileh));
+ }
+}
+
+
+// lesser-used data in canvas item, plus room for extension.
+// Be careful adding to this - check all usages.
+class Q3CanvasItemExtra {
+ Q3CanvasItemExtra() : vx(0.0), vy(0.0) { }
+ double vx,vy;
+ friend class Q3CanvasItem;
+};
+
+
+/*!
+ \class Q3CanvasItem
+ \compat
+ \brief The Q3CanvasItem class provides an abstract graphic object on a Q3Canvas.
+
+ A variety of Q3CanvasItem subclasses provide immediately usable
+ behaviour. This class is a pure abstract superclass providing the
+ behaviour that is shared among all the concrete canvas item classes.
+ Q3CanvasItem is not intended for direct subclassing. It is much easier
+ to subclass one of its subclasses, e.g. Q3CanvasPolygonalItem (the
+ commonest base class), Q3CanvasRectangle, Q3CanvasSprite, Q3CanvasEllipse
+ or Q3CanvasText.
+
+ Canvas items are added to a canvas by constructing them and passing the
+ canvas to the canvas item's constructor. An item can be moved to a
+ different canvas using setCanvas().
+
+ Items appear on the canvas after their \link show() show()\endlink
+ function has been called (or \link setVisible()
+ setVisible(true)\endlink), and \e after update() has been called. The
+ canvas only shows items that are \link setVisible() visible\endlink,
+ and then only if \l update() is called. If you created the canvas
+ without passing a width and height to the constructor you'll also need
+ to call \link Q3Canvas::resize() resize()\endlink. Since the canvas
+ background defaults to white and canvas items default to white,
+ you may need to change colors to see your items.
+
+ A Q3CanvasItem object can be moved in the x(), y() and z() dimensions
+ using functions such as move(), moveBy(), setX(), setY() and setZ(). A
+ canvas item can be set in motion, `animated', using setAnimated() and
+ given a velocity in the x and y directions with setXVelocity() and
+ setYVelocity() -- the same effect can be achieved by calling
+ setVelocity(). Use the collidesWith() function to see if the canvas item
+ will collide on the \e next advance(1) and use collisions() to see what
+ collisions have occurred.
+
+ Use Q3CanvasSprite or your own subclass of Q3CanvasSprite to create canvas
+ items which are animated, i.e. which change over time.
+
+ The size of a canvas item is given by boundingRect(). Use
+ boundingRectAdvanced() to see what the size of the canvas item will be
+ \e after the next advance(1) call.
+
+ The rtti() function is used for identifying subclasses of Q3CanvasItem.
+ The canvas() function returns a pointer to the canvas which contains the
+ canvas item.
+
+ Q3CanvasItem provides the show() and isVisible() functions like those in
+ QWidget.
+
+ Q3CanvasItem also provides the setEnabled(), setActive() and
+ setSelected() functions; these functions set the relevant boolean and
+ cause a repaint but the boolean values they set are not used in
+ Q3CanvasItem itself. You can make use of these booleans in your subclasses.
+
+ By default, canvas items have no velocity, no size, and are not in
+ motion. The subclasses provided in Qt do not change these defaults
+ except where noted.
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ \enum Q3CanvasItem::RttiValues
+
+ This enum is used to name the different types of canvas item.
+
+ \value Rtti_Item Canvas item abstract base class
+ \value Rtti_Ellipse
+ \value Rtti_Line
+ \value Rtti_Polygon
+ \value Rtti_PolygonalItem
+ \value Rtti_Rectangle
+ \value Rtti_Spline
+ \value Rtti_Sprite
+ \value Rtti_Text
+
+*/
+
+/*!
+ \fn void Q3CanvasItem::update()
+
+ Call this function to repaint the canvas's changed chunks.
+*/
+
+/*!
+ Constructs a Q3CanvasItem on canvas \a canvas.
+
+ \sa setCanvas()
+*/
+Q3CanvasItem::Q3CanvasItem(Q3Canvas* canvas) :
+ cnv(canvas),
+ myx(0),myy(0),myz(0)
+{
+ ani=0;
+ vis=0;
+ val=0;
+ sel=0;
+ ena=0;
+ act=0;
+
+ ext = 0;
+ if (cnv) cnv->addItem(this);
+}
+
+/*!
+ Destroys the Q3CanvasItem and removes it from its canvas.
+*/
+Q3CanvasItem::~Q3CanvasItem()
+{
+ if (cnv) {
+ cnv->removeItem(this);
+ cnv->removeAnimation(this);
+ }
+ delete ext;
+}
+
+Q3CanvasItemExtra& Q3CanvasItem::extra()
+{
+ if (!ext)
+ ext = new Q3CanvasItemExtra;
+ return *ext;
+}
+
+/*!
+ \fn double Q3CanvasItem::x() const
+
+ Returns the horizontal position of the canvas item. Note that
+ subclasses often have an origin other than the top-left corner.
+*/
+
+/*!
+ \fn double Q3CanvasItem::y() const
+
+ Returns the vertical position of the canvas item. Note that
+ subclasses often have an origin other than the top-left corner.
+*/
+
+/*!
+ \fn double Q3CanvasItem::z() const
+
+ Returns the z index of the canvas item, which is used for visual
+ order: higher-z items obscure (are in front of) lower-z items.
+*/
+
+/*!
+ \fn void Q3CanvasItem::setX(double x)
+
+ Moves the canvas item so that its x-position is \a x.
+
+ \sa x(), move()
+*/
+
+/*!
+ \fn void Q3CanvasItem::setY(double y)
+
+ Moves the canvas item so that its y-position is \a y.
+
+ \sa y(), move()
+*/
+
+/*!
+ \fn void Q3CanvasItem::setZ(double z)
+
+ Sets the z index of the canvas item to \a z. Higher-z items
+ obscure (are in front of) lower-z items.
+
+ \sa z(), move()
+*/
+
+
+/*!
+ Moves the canvas item relative to its current position by (\a dx,
+ \a dy).
+*/
+void Q3CanvasItem::moveBy(double dx, double dy)
+{
+ if (dx || dy) {
+ removeFromChunks();
+ myx += dx;
+ myy += dy;
+ addToChunks();
+ }
+}
+
+
+/*!
+ Moves the canvas item to the absolute position (\a x, \a y).
+*/
+void Q3CanvasItem::move(double x, double y)
+{
+ moveBy(x-myx, y-myy);
+}
+
+
+/*!
+ Returns true if the canvas item is in motion; otherwise returns
+ false.
+
+ \sa setVelocity(), setAnimated()
+*/
+bool Q3CanvasItem::animated() const
+{
+ return (bool)ani;
+}
+
+/*!
+ Sets the canvas item to be in motion if \a y is true, or not if \a
+ y is false. The speed and direction of the motion is set with
+ setVelocity(), or with setXVelocity() and setYVelocity().
+
+ \sa advance(), Q3Canvas::advance()
+*/
+void Q3CanvasItem::setAnimated(bool y)
+{
+ if (y != (bool)ani) {
+ ani = (uint)y;
+ if (y) {
+ cnv->addAnimation(this);
+ } else {
+ cnv->removeAnimation(this);
+ }
+ }
+}
+
+/*!
+ \fn void Q3CanvasItem::setXVelocity(double vx)
+
+ Sets the horizontal component of the canvas item's velocity to \a vx.
+
+ \sa setYVelocity() setVelocity()
+*/
+
+/*!
+ \fn void Q3CanvasItem::setYVelocity(double vy)
+
+ Sets the vertical component of the canvas item's velocity to \a vy.
+
+ \sa setXVelocity() setVelocity()
+*/
+
+/*!
+ Sets the canvas item to be in motion, moving by \a vx and \a vy
+ pixels in the horizontal and vertical directions respectively.
+
+ \sa advance() setXVelocity() setYVelocity()
+*/
+void Q3CanvasItem::setVelocity(double vx, double vy)
+{
+ if (ext || vx!=0.0 || vy!=0.0) {
+ if (!ani)
+ setAnimated(true);
+ extra().vx = vx;
+ extra().vy = vy;
+ }
+}
+
+/*!
+ Returns the horizontal velocity component of the canvas item.
+*/
+double Q3CanvasItem::xVelocity() const
+{
+ return ext ? ext->vx : 0;
+}
+
+/*!
+ Returns the vertical velocity component of the canvas item.
+*/
+double Q3CanvasItem::yVelocity() const
+{
+ return ext ? ext->vy : 0;
+}
+
+/*!
+ The default implementation moves the canvas item, if it is
+ animated(), by the preset velocity if \a phase is 1, and does
+ nothing if \a phase is 0.
+
+ Note that if you reimplement this function, the reimplementation
+ must not change the canvas in any way, for example it must not add
+ or remove items.
+
+ \sa Q3Canvas::advance() setVelocity()
+*/
+void Q3CanvasItem::advance(int phase)
+{
+ if (ext && phase==1)
+ moveBy(ext->vx,ext->vy);
+}
+
+/*!
+ \fn void Q3CanvasItem::draw(QPainter& painter)
+
+ This abstract virtual function draws the canvas item using \a painter.
+*/
+
+/*!
+ Sets the Q3Canvas upon which the canvas item is to be drawn to \a c.
+
+ \sa canvas()
+*/
+void Q3CanvasItem::setCanvas(Q3Canvas* c)
+{
+ bool v=isVisible();
+ setVisible(false);
+ if (cnv) {
+ if (ext)
+ cnv->removeAnimation(this);
+ cnv->removeItem(this);
+ }
+ cnv=c;
+ if (cnv) {
+ cnv->addItem(this);
+ if (ext)
+ cnv->addAnimation(this);
+ }
+ setVisible(v);
+}
+
+/*!
+ \fn Q3Canvas* Q3CanvasItem::canvas() const
+
+ Returns the canvas containing the canvas item.
+*/
+
+/*! Shorthand for setVisible(true). */
+void Q3CanvasItem::show()
+{
+ setVisible(true);
+}
+
+/*! Shorthand for setVisible(false). */
+void Q3CanvasItem::hide()
+{
+ setVisible(false);
+}
+
+/*!
+ Makes the canvas item visible if \a yes is true, or invisible if
+ \a yes is false. The change takes effect when Q3Canvas::update() is
+ next called.
+*/
+void Q3CanvasItem::setVisible(bool yes)
+{
+ if ((bool)vis!=yes) {
+ if (yes) {
+ vis=(uint)yes;
+ addToChunks();
+ } else {
+ removeFromChunks();
+ vis=(uint)yes;
+ }
+ }
+}
+/*!
+ \obsolete
+ \fn bool Q3CanvasItem::visible() const
+ Use isVisible() instead.
+*/
+
+/*!
+ \fn bool Q3CanvasItem::isVisible() const
+
+ Returns true if the canvas item is visible; otherwise returns
+ false.
+
+ Note that in this context true does \e not mean that the canvas
+ item is currently in a view, merely that if a view is showing the
+ area where the canvas item is positioned, and the item is not
+ obscured by items with higher z values, and the view is not
+ obscured by overlaying windows, it would be visible.
+
+ \sa setVisible(), z()
+*/
+
+/*!
+ \obsolete
+ \fn bool Q3CanvasItem::selected() const
+ Use isSelected() instead.
+*/
+
+/*!
+ \fn bool Q3CanvasItem::isSelected() const
+
+ Returns true if the canvas item is selected; otherwise returns false.
+*/
+
+/*!
+ Sets the selected flag of the item to \a yes. If this changes the
+ item's selected state the item will be redrawn when
+ Q3Canvas::update() is next called.
+
+ The Q3Canvas, Q3CanvasItem and the Qt-supplied Q3CanvasItem
+ subclasses do not make use of this value. The setSelected()
+ function is supplied because many applications need it, but it is
+ up to you how you use the isSelected() value.
+*/
+void Q3CanvasItem::setSelected(bool yes)
+{
+ if ((bool)sel!=yes) {
+ sel=(uint)yes;
+ changeChunks();
+ }
+}
+
+/*!
+ \obsolete
+ \fn bool Q3CanvasItem::enabled() const
+ Use isEnabled() instead.
+*/
+
+/*!
+ \fn bool Q3CanvasItem::isEnabled() const
+
+ Returns true if the Q3CanvasItem is enabled; otherwise returns false.
+*/
+
+/*!
+ Sets the enabled flag of the item to \a yes. If this changes the
+ item's enabled state the item will be redrawn when
+ Q3Canvas::update() is next called.
+
+ The Q3Canvas, Q3CanvasItem and the Qt-supplied Q3CanvasItem
+ subclasses do not make use of this value. The setEnabled()
+ function is supplied because many applications need it, but it is
+ up to you how you use the isEnabled() value.
+*/
+void Q3CanvasItem::setEnabled(bool yes)
+{
+ if (ena!=(uint)yes) {
+ ena=(uint)yes;
+ changeChunks();
+ }
+}
+
+/*!
+ \obsolete
+ \fn bool Q3CanvasItem::active() const
+ Use isActive() instead.
+*/
+
+/*!
+ \fn bool Q3CanvasItem::isActive() const
+
+ Returns true if the Q3CanvasItem is active; otherwise returns false.
+*/
+
+/*!
+ Sets the active flag of the item to \a yes. If this changes the
+ item's active state the item will be redrawn when
+ Q3Canvas::update() is next called.
+
+ The Q3Canvas, Q3CanvasItem and the Qt-supplied Q3CanvasItem
+ subclasses do not make use of this value. The setActive() function
+ is supplied because many applications need it, but it is up to you
+ how you use the isActive() value.
+*/
+void Q3CanvasItem::setActive(bool yes)
+{
+ if (act!=(uint)yes) {
+ act=(uint)yes;
+ changeChunks();
+ }
+}
+
+bool qt_testCollision(const Q3CanvasSprite* s1, const Q3CanvasSprite* s2)
+{
+ const QImage* s2image = s2->imageAdvanced()->collision_mask;
+ QRect s2area = s2->boundingRectAdvanced();
+
+ QRect cyourarea(s2area.x(),s2area.y(),
+ s2area.width(),s2area.height());
+
+ QImage* s1image=s1->imageAdvanced()->collision_mask;
+
+ QRect s1area = s1->boundingRectAdvanced();
+
+ QRect ourarea = s1area.intersected(cyourarea);
+
+ if (ourarea.isEmpty())
+ return false;
+
+ int x2=ourarea.x()-cyourarea.x();
+ int y2=ourarea.y()-cyourarea.y();
+ int x1=ourarea.x()-s1area.x();
+ int y1=ourarea.y()-s1area.y();
+ int w=ourarea.width();
+ int h=ourarea.height();
+
+ if (!s2image) {
+ if (!s1image)
+ return w>0 && h>0;
+ // swap everything around
+ int t;
+ t=x1; x1=x2; x2=t;
+ t=y1; x1=y2; y2=t;
+ s2image = s1image;
+ s1image = 0;
+ }
+
+ // s2image != 0
+
+ // A non-linear search may be more efficient.
+ // Perhaps spiralling out from the center, or a simpler
+ // vertical expansion from the centreline.
+
+ // We assume that sprite masks don't have
+ // different bit orders.
+ //
+ // Q_ASSERT(s1image->bitOrder()==s2image->bitOrder());
+
+ if (s1image) {
+ if (s1image->bitOrder() == QImage::LittleEndian) {
+ for (int j=0; j<h; j++) {
+ uchar* ml = s1image->scanLine(y1+j);
+ const uchar* yl = s2image->scanLine(y2+j);
+ for (int i=0; i<w; i++) {
+ if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7))
+ && *(ml + ((x1+i) >> 3)) & (1 << ((x1+i) & 7)))
+ {
+ return true;
+ }
+ }
+ }
+ } else {
+ for (int j=0; j<h; j++) {
+ uchar* ml = s1image->scanLine(y1+j);
+ const uchar* yl = s2image->scanLine(y2+j);
+ for (int i=0; i<w; i++) {
+ if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7)))
+ && *(ml + ((x1+i) >> 3)) & (1 << (7-((x1+i) & 7))))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ } else {
+ if (s2image->bitOrder() == QImage::LittleEndian) {
+ for (int j=0; j<h; j++) {
+ const uchar* yl = s2image->scanLine(y2+j);
+ for (int i=0; i<w; i++) {
+ if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7)))
+ {
+ return true;
+ }
+ }
+ }
+ } else {
+ for (int j=0; j<h; j++) {
+ const uchar* yl = s2image->scanLine(y2+j);
+ for (int i=0; i<w; i++) {
+ if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7))))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool collision_double_dispatch(const Q3CanvasSprite* s1,
+ const Q3CanvasPolygonalItem* p1,
+ const Q3CanvasRectangle* r1,
+ const Q3CanvasEllipse* e1,
+ const Q3CanvasText* t1,
+ const Q3CanvasSprite* s2,
+ const Q3CanvasPolygonalItem* p2,
+ const Q3CanvasRectangle* r2,
+ const Q3CanvasEllipse* e2,
+ const Q3CanvasText* t2)
+{
+ const Q3CanvasItem* i1 = s1 ?
+ (const Q3CanvasItem*)s1 : p1 ?
+ (const Q3CanvasItem*)p1 : r1 ?
+ (const Q3CanvasItem*)r1 : e1 ?
+ (const Q3CanvasItem*)e1 : (const Q3CanvasItem*)t1;
+ const Q3CanvasItem* i2 = s2 ?
+ (const Q3CanvasItem*)s2 : p2 ?
+ (const Q3CanvasItem*)p2 : r2 ?
+ (const Q3CanvasItem*)r2 : e2 ?
+ (const Q3CanvasItem*)e2 : (const Q3CanvasItem*)t2;
+
+ if (s1 && s2) {
+ // a
+ return qt_testCollision(s1,s2);
+ } else if ((r1 || t1 || s1) && (r2 || t2 || s2)) {
+ // b
+ QRect rc1 = i1->boundingRectAdvanced();
+ QRect rc2 = i2->boundingRectAdvanced();
+ return rc1.intersects(rc2);
+ } else if (e1 && e2
+ && e1->angleLength()>=360*16 && e2->angleLength()>=360*16
+ && e1->width()==e1->height()
+ && e2->width()==e2->height()) {
+ // c
+ double xd = (e1->x()+e1->xVelocity())-(e2->x()+e1->xVelocity());
+ double yd = (e1->y()+e1->yVelocity())-(e2->y()+e1->yVelocity());
+ double rd = (e1->width()+e2->width())/2;
+ return xd*xd+yd*yd <= rd*rd;
+ } else if (p1 && (p2 || s2 || t2)) {
+ // d
+ Q3PointArray pa1 = p1->areaPointsAdvanced();
+ Q3PointArray pa2 = p2 ? p2->areaPointsAdvanced()
+ : Q3PointArray(i2->boundingRectAdvanced());
+ bool col= !(QRegion(pa1) & QRegion(pa2,true)).isEmpty();
+
+ return col;
+ } else {
+ return collision_double_dispatch(s2,p2,r2,e2,t2,
+ s1,p1,r1,e1,t1);
+ }
+}
+
+/*!
+ \fn bool Q3CanvasItem::collidesWith(const Q3CanvasItem* other) const
+
+ Returns true if the canvas item will collide with the \a other
+ item \e after they have moved by their current velocities;
+ otherwise returns false.
+
+ \sa collisions()
+*/
+
+
+/*!
+ \class Q3CanvasSprite
+ \compat
+ \brief The Q3CanvasSprite class provides an animated canvas item on a Q3Canvas.
+
+ A canvas sprite is an object which can contain any number of images
+ (referred to as frames), only one of which is current, i.e.
+ displayed, at any one time. The images can be passed in the
+ constructor or set or changed later with setSequence(). If you
+ subclass Q3CanvasSprite you can change the frame that is displayed
+ periodically, e.g. whenever Q3CanvasItem::advance(1) is called to
+ create the effect of animation.
+
+ The current frame can be set with setFrame() or with move(). The
+ number of frames available is given by frameCount(). The bounding
+ rectangle of the current frame is returned by boundingRect().
+
+ The current frame's image can be retrieved with image(); use
+ imageAdvanced() to retrieve the image for the frame that will be
+ shown after advance(1) is called. Use the image() overload passing
+ it an integer index to retrieve a particular image from the list of
+ frames.
+
+ Use width() and height() to retrieve the dimensions of the current
+ frame.
+
+ Use leftEdge() and rightEdge() to retrieve the current frame's
+ left-hand and right-hand x-coordinates respectively. Use
+ bottomEdge() and topEdge() to retrieve the current frame's bottom
+ and top y-coordinates respectively. These functions have an overload
+ which will accept an integer frame number to retrieve the
+ coordinates of a particular frame.
+
+ Q3CanvasSprite draws very quickly, at the expense of memory.
+
+ The current frame's image can be drawn on a painter with draw().
+
+ Like any other canvas item, canvas sprites can be moved with
+ move() which sets the x and y coordinates and the frame number, as
+ well as with Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by
+ setting coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY()
+ and Q3CanvasItem::setZ().
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+
+/*!
+ \reimp
+*/
+bool Q3CanvasSprite::collidesWith(const Q3CanvasItem* i) const
+{
+ return i->collidesWith(this,0,0,0,0);
+}
+
+/*!
+ Returns true if the canvas item collides with any of the given
+ items; otherwise returns false. The parameters, \a s, \a p, \a r,
+ \a e and \a t, are all the same object, this is just a type
+ resolution trick.
+*/
+bool Q3CanvasSprite::collidesWith(const Q3CanvasSprite* s,
+ const Q3CanvasPolygonalItem* p,
+ const Q3CanvasRectangle* r,
+ const Q3CanvasEllipse* e,
+ const Q3CanvasText* t) const
+{
+ return collision_double_dispatch(s,p,r,e,t,this,0,0,0,0);
+}
+
+/*!
+ \reimp
+*/
+bool Q3CanvasPolygonalItem::collidesWith(const Q3CanvasItem* i) const
+{
+ return i->collidesWith(0,this,0,0,0);
+}
+
+bool Q3CanvasPolygonalItem::collidesWith( const Q3CanvasSprite* s,
+ const Q3CanvasPolygonalItem* p,
+ const Q3CanvasRectangle* r,
+ const Q3CanvasEllipse* e,
+ const Q3CanvasText* t) const
+{
+ return collision_double_dispatch(s,p,r,e,t,0,this,0,0,0);
+}
+
+/*!
+ \reimp
+*/
+bool Q3CanvasRectangle::collidesWith(const Q3CanvasItem* i) const
+{
+ return i->collidesWith(0,this,this,0,0);
+}
+
+bool Q3CanvasRectangle::collidesWith( const Q3CanvasSprite* s,
+ const Q3CanvasPolygonalItem* p,
+ const Q3CanvasRectangle* r,
+ const Q3CanvasEllipse* e,
+ const Q3CanvasText* t) const
+{
+ return collision_double_dispatch(s,p,r,e,t,0,this,this,0,0);
+}
+
+
+/*!
+ \reimp
+*/
+bool Q3CanvasEllipse::collidesWith(const Q3CanvasItem* i) const
+{
+ return i->collidesWith(0,this,0,this,0);
+}
+
+bool Q3CanvasEllipse::collidesWith( const Q3CanvasSprite* s,
+ const Q3CanvasPolygonalItem* p,
+ const Q3CanvasRectangle* r,
+ const Q3CanvasEllipse* e,
+ const Q3CanvasText* t) const
+{
+ return collision_double_dispatch(s,p,r,e,t,0,this,0,this,0);
+}
+
+/*!
+ \reimp
+*/
+bool Q3CanvasText::collidesWith(const Q3CanvasItem* i) const
+{
+ return i->collidesWith(0,0,0,0,this);
+}
+
+bool Q3CanvasText::collidesWith( const Q3CanvasSprite* s,
+ const Q3CanvasPolygonalItem* p,
+ const Q3CanvasRectangle* r,
+ const Q3CanvasEllipse* e,
+ const Q3CanvasText* t) const
+{
+ return collision_double_dispatch(s,p,r,e,t,0,0,0,0,this);
+}
+
+/*!
+ Returns the list of canvas items that this canvas item has
+ collided with.
+
+ A collision is generally defined as occurring when the pixels of
+ one item draw on the pixels of another item, but not all
+ subclasses are so precise. Also, since pixel-wise collision
+ detection can be slow, this function works in either exact or
+ inexact mode, according to the \a exact parameter.
+
+ If \a exact is true, the canvas items returned have been
+ accurately tested for collision with the canvas item.
+
+ If \a exact is false, the canvas items returned are \e near the
+ canvas item. You can test the canvas items returned using
+ collidesWith() if any are interesting collision candidates. By
+ using this approach, you can ignore some canvas items for which
+ collisions are not relevant.
+
+ The returned list is a list of Q3CanvasItems, but often you will
+ need to cast the items to their subclass types. The safe way to do
+ this is to use rtti() before casting. This provides some of the
+ functionality of the standard C++ dynamic cast operation even on
+ compilers where dynamic casts are not available.
+
+ Note that a canvas item may be `on' a canvas, e.g. it was created
+ with the canvas as parameter, even though its coordinates place it
+ beyond the edge of the canvas's area. Collision detection only
+ works for canvas items which are wholly or partly within the
+ canvas's area.
+
+ Note that if items have a velocity (see \l setVelocity()), then
+ collision testing is done based on where the item \e will be when
+ it moves, not its current location. For example, a "ball" item
+ doesn't need to actually embed into a "wall" item before a
+ collision is detected. For items without velocity, plain
+ intersection is used.
+*/
+Q3CanvasItemList Q3CanvasItem::collisions(bool exact) const
+{
+ return canvas()->collisions(chunks(),this,exact);
+}
+
+/*!
+ Returns a list of canvas items that collide with the point \a p.
+ The list is ordered by z coordinates, from highest z coordinate
+ (front-most item) to lowest z coordinate (rear-most item).
+*/
+Q3CanvasItemList Q3Canvas::collisions(const QPoint& p) const
+{
+ return collisions(QRect(p,QSize(1,1)));
+}
+
+/*!
+ \overload
+
+ Returns a list of items which collide with the rectangle \a r. The
+ list is ordered by z coordinates, from highest z coordinate
+ (front-most item) to lowest z coordinate (rear-most item).
+*/
+Q3CanvasItemList Q3Canvas::collisions(const QRect& r) const
+{
+ Q3CanvasRectangle i(r,(Q3Canvas*)this);
+ i.setPen(NoPen);
+ i.show(); // doesn't actually show, since we destroy it
+ Q3CanvasItemList l = i.collisions(true);
+ l.sort();
+ return l;
+}
+
+/*!
+ \overload
+
+ Returns a list of canvas items which intersect with the chunks
+ listed in \a chunklist, excluding \a item. If \a exact is true,
+ only those which actually \link Q3CanvasItem::collidesWith()
+ collide with\endlink \a item are returned; otherwise canvas items
+ are included just for being in the chunks.
+
+ This is a utility function mainly used to implement the simpler
+ Q3CanvasItem::collisions() function.
+*/
+Q3CanvasItemList Q3Canvas::collisions(const Q3PointArray& chunklist,
+ const Q3CanvasItem* item, bool exact) const
+{
+ Q3PtrDict<void> seen;
+ Q3CanvasItemList result;
+ for (int i=0; i<(int)chunklist.count(); i++) {
+ int x = chunklist[i].x();
+ int y = chunklist[i].y();
+ if (validChunk(x,y)) {
+ const Q3CanvasItemList* l = chunk(x,y).listPtr();
+ for (Q3CanvasItemList::ConstIterator it=l->begin(); it!=l->end(); ++it) {
+ Q3CanvasItem *g=*it;
+ if (g != item) {
+ if (!seen.find(g)) {
+ seen.replace(g,(void*)1);
+ if (!exact || item->collidesWith(g))
+ result.append(g);
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
+
+/*!
+ \internal
+ Adds the item to all the chunks it covers.
+*/
+void Q3CanvasItem::addToChunks()
+{
+ if (isVisible() && canvas()) {
+ Q3PointArray pa = chunks();
+ for (int i=0; i<(int)pa.count(); i++)
+ canvas()->addItemToChunk(this,pa[i].x(),pa[i].y());
+ val=(uint)true;
+ }
+}
+
+/*!
+ \internal
+ Removes the item from all the chunks it covers.
+*/
+void Q3CanvasItem::removeFromChunks()
+{
+ if (isVisible() && canvas()) {
+ Q3PointArray pa = chunks();
+ for (int i=0; i<(int)pa.count(); i++)
+ canvas()->removeItemFromChunk(this,pa[i].x(),pa[i].y());
+ }
+}
+
+/*!
+ \internal
+ Sets all the chunks covered by the item to be refreshed with Q3Canvas::update()
+ is next called.
+*/
+void Q3CanvasItem::changeChunks()
+{
+ if (isVisible() && canvas()) {
+ if (!val)
+ addToChunks();
+ Q3PointArray pa = chunks();
+ for (int i=0; i<(int)pa.count(); i++)
+ canvas()->setChangedChunk(pa[i].x(),pa[i].y());
+ }
+}
+
+/*!
+ \fn QRect Q3CanvasItem::boundingRect() const
+
+ Returns the bounding rectangle in pixels that the canvas item covers.
+
+ \sa boundingRectAdvanced()
+*/
+
+/*!
+ Returns the bounding rectangle of pixels that the canvas item \e
+ will cover after advance(1) is called.
+
+ \sa boundingRect()
+*/
+QRect Q3CanvasItem::boundingRectAdvanced() const
+{
+ int dx = int(x()+xVelocity())-int(x());
+ int dy = int(y()+yVelocity())-int(y());
+ QRect r = boundingRect();
+ r.moveBy(dx,dy);
+ return r;
+}
+
+/*!
+ \class Q3CanvasPixmap
+ \compat
+ \brief The Q3CanvasPixmap class provides pixmaps for Q3CanvasSprites.
+
+ If you want to show a single pixmap on a Q3Canvas use a
+ Q3CanvasSprite with just one pixmap.
+
+ When pixmaps are inserted into a Q3CanvasPixmapArray they are held
+ as Q3CanvasPixmaps. \l{Q3CanvasSprite}s are used to show pixmaps on
+ \l{Q3Canvas}es and hold their pixmaps in a Q3CanvasPixmapArray. If
+ you retrieve a frame (pixmap) from a Q3CanvasSprite it will be
+ returned as a Q3CanvasPixmap.
+
+ The pixmap is a QPixmap and can only be set in the constructor.
+ There are three different constructors, one taking a QPixmap, one
+ a QImage and one a file name that refers to a file in any
+ supported file format (see QImageReader).
+
+ Q3CanvasPixmap can have a hotspot which is defined in terms of an (x,
+ y) offset. When you create a Q3CanvasPixmap from a PNG file or from
+ a QImage that has a QImage::offset(), the offset() is initialized
+ appropriately, otherwise the constructor leaves it at (0, 0). You
+ can set it later using setOffset(). When the Q3CanvasPixmap is used
+ in a Q3CanvasSprite, the offset position is the point at
+ Q3CanvasItem::x() and Q3CanvasItem::y(), not the top-left corner of
+ the pixmap.
+
+ Note that for Q3CanvasPixmap objects created by a Q3CanvasSprite, the
+ position of each Q3CanvasPixmap object is set so that the hotspot
+ stays in the same position.
+
+ \sa Q3CanvasPixmapArray Q3CanvasItem Q3CanvasSprite, QtCanvas, {Porting to Graphics View}
+*/
+
+#ifndef QT_NO_IMAGEIO
+
+/*!
+ Constructs a Q3CanvasPixmap that uses the image stored in \a
+ datafilename.
+*/
+Q3CanvasPixmap::Q3CanvasPixmap(const QString& datafilename)
+{
+ QImage image(datafilename);
+ init(image);
+}
+
+#endif
+
+/*!
+ Constructs a Q3CanvasPixmap from the image \a image.
+*/
+Q3CanvasPixmap::Q3CanvasPixmap(const QImage& image)
+{
+ init(image);
+}
+/*!
+ Constructs a Q3CanvasPixmap from the pixmap \a pm using the offset
+ \a offset.
+*/
+Q3CanvasPixmap::Q3CanvasPixmap(const QPixmap& pm, const QPoint& offset)
+{
+ init(pm,offset.x(),offset.y());
+}
+
+void Q3CanvasPixmap::init(const QImage& image)
+{
+ convertFromImage(image);
+ hotx = image.offset().x();
+ hoty = image.offset().y();
+#ifndef QT_NO_IMAGE_DITHER_TO_1
+ if(image.hasAlphaBuffer()) {
+ QImage i = image.createAlphaMask();
+ collision_mask = new QImage(i);
+ } else
+#endif
+ collision_mask = 0;
+}
+
+void Q3CanvasPixmap::init(const QPixmap& pixmap, int hx, int hy)
+{
+ (QPixmap&)*this = pixmap;
+ hotx = hx;
+ hoty = hy;
+ if(pixmap.hasAlphaChannel()) {
+ QImage i = mask().convertToImage();
+ collision_mask = new QImage(i);
+ } else
+ collision_mask = 0;
+}
+
+/*!
+ Destroys the pixmap.
+*/
+Q3CanvasPixmap::~Q3CanvasPixmap()
+{
+ delete collision_mask;
+}
+
+/*!
+ \fn int Q3CanvasPixmap::offsetX() const
+
+ Returns the x-offset of the pixmap's hotspot.
+
+ \sa setOffset()
+*/
+
+/*!
+ \fn int Q3CanvasPixmap::offsetY() const
+
+ Returns the y-offset of the pixmap's hotspot.
+
+ \sa setOffset()
+*/
+
+/*!
+ \fn void Q3CanvasPixmap::setOffset(int x, int y)
+
+ Sets the offset of the pixmap's hotspot to (\a x, \a y).
+
+ \warning Do not call this function if any Q3CanvasSprites are
+ currently showing this pixmap.
+*/
+
+/*!
+ \class Q3CanvasPixmapArray
+ \compat
+ \brief The Q3CanvasPixmapArray class provides an array of Q3CanvasPixmaps.
+
+ This class is used by Q3CanvasSprite to hold an array of pixmaps.
+ It is used to implement animated sprites, i.e. images that change
+ over time, with each pixmap in the array holding one frame.
+
+ Depending on the constructor you use you can load multiple pixmaps
+ into the array either from a directory (specifying a wildcard
+ pattern for the files), or from a list of QPixmaps. You can also
+ read in a set of pixmaps after construction using readPixmaps().
+
+ Individual pixmaps can be set with setImage() and retrieved with
+ image(). The number of pixmaps in the array is returned by
+ count().
+
+ Q3CanvasSprite uses an image's mask for collision detection. You
+ can change this by reading in a separate set of image masks using
+ readCollisionMasks().
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Constructs an invalid array (i.e. isValid() will return false).
+ You must call readPixmaps() before being able to use this
+ Q3CanvasPixmapArray.
+*/
+Q3CanvasPixmapArray::Q3CanvasPixmapArray()
+: framecount(0), img(0)
+{
+}
+
+#ifndef QT_NO_IMAGEIO
+/*!
+ Constructs a Q3CanvasPixmapArray from files.
+
+ The \a fc parameter sets the number of frames to be loaded for
+ this image.
+
+ If \a fc is not 0, \a datafilenamepattern should contain "%1",
+ e.g. "foo%1.png". The actual filenames are formed by replacing the
+ %1 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
+ foo0001.png, foo0002.png, etc.
+
+ If \a fc is 0, \a datafilenamepattern is asssumed to be a
+ filename, and the image contained in this file will be loaded as
+ the first (and only) frame.
+
+ If \a datafilenamepattern does not exist, is not readable, isn't
+ an image, or some other error occurs, the array ends up empty and
+ isValid() returns false.
+*/
+
+Q3CanvasPixmapArray::Q3CanvasPixmapArray(const QString& datafilenamepattern,
+ int fc)
+: framecount(0), img(0)
+{
+ readPixmaps(datafilenamepattern,fc);
+}
+#endif
+
+/*!
+ \obsolete
+ Use Q3CanvasPixmapArray::Q3CanvasPixmapArray(Q3ValueList<QPixmap>, Q3PointArray)
+ instead.
+
+ Constructs a Q3CanvasPixmapArray from the list of QPixmaps \a
+ list. The \a hotspots list has to be of the same size as \a list.
+*/
+Q3CanvasPixmapArray::Q3CanvasPixmapArray(Q3PtrList<QPixmap> list, Q3PtrList<QPoint> hotspots) :
+ framecount(list.count()),
+ img(new Q3CanvasPixmap*[list.count()])
+{
+ if (list.count() != hotspots.count()) {
+ qWarning("Q3CanvasPixmapArray: lists have different lengths");
+ reset();
+ img = 0;
+ } else {
+ list.first();
+ hotspots.first();
+ for (int i=0; i<framecount; i++) {
+ img[i]=new Q3CanvasPixmap(*list.current(), *hotspots.current());
+ list.next();
+ hotspots.next();
+ }
+ }
+}
+
+/*!
+ Constructs a Q3CanvasPixmapArray from the list of QPixmaps in the
+ \a list. Each pixmap will get a hotspot according to the \a
+ hotspots array. If no hotspots are specified, each one is set to
+ be at position (0, 0).
+
+ If an error occurs, isValid() will return false.
+*/
+Q3CanvasPixmapArray::Q3CanvasPixmapArray(Q3ValueList<QPixmap> list, Q3PointArray hotspots) :
+ framecount((int)list.size()),
+ img(new Q3CanvasPixmap*[list.size()])
+{
+ bool have_hotspots = (hotspots.size() != 0);
+ if (have_hotspots && list.count() != hotspots.count()) {
+ qWarning("Q3CanvasPixmapArray: lists have different lengths");
+ reset();
+ img = 0;
+ } else {
+ Q3ValueList<QPixmap>::iterator it;
+ it = list.begin();
+ for (int i=0; i<framecount; i++) {
+ QPoint hs = have_hotspots ? hotspots[i] : QPoint(0, 0);
+ img[i]=new Q3CanvasPixmap(*it, hs);
+ ++it;
+ }
+ }
+}
+
+/*!
+ Destroys the pixmap array and all the pixmaps it contains.
+*/
+Q3CanvasPixmapArray::~Q3CanvasPixmapArray()
+{
+ reset();
+}
+
+void Q3CanvasPixmapArray::reset()
+{
+ for (int i=0; i<framecount; i++)
+ delete img[i];
+ delete [] img;
+ img = 0;
+ framecount = 0;
+}
+
+#ifndef QT_NO_IMAGEIO
+/*!
+ Reads one or more pixmaps into the pixmap array.
+
+ If \a fc is not 0, \a filenamepattern should contain "%1", e.g.
+ "foo%1.png". The actual filenames are formed by replacing the %1
+ with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
+ foo0001.png, foo0002.png, etc.
+
+ If \a fc is 0, \a filenamepattern is asssumed to be a filename,
+ and the image contained in this file will be loaded as the first
+ (and only) frame.
+
+ If \a filenamepattern does not exist, is not readable, isn't an
+ image, or some other error occurs, this function will return
+ false, and isValid() will return false; otherwise this function
+ will return true.
+
+ \sa isValid()
+*/
+bool Q3CanvasPixmapArray::readPixmaps(const QString& filenamepattern,
+ int fc)
+{
+ return readPixmaps(filenamepattern,fc,false);
+}
+
+/*!
+ Reads new collision masks for the array.
+
+ By default, Q3CanvasSprite uses the image mask of a sprite to
+ detect collisions. Use this function to set your own collision
+ image masks.
+
+ If count() is 1 \a filename must specify a real filename to read
+ the mask from. If count() is greater than 1, the \a filename must
+ contain a "%1" that will get replaced by the number of the mask to
+ be loaded, just like Q3CanvasPixmapArray::readPixmaps().
+
+ All collision masks must be 1-bit images or this function call
+ will fail.
+
+ If the file isn't readable, contains the wrong number of images,
+ or there is some other error, this function will return false, and
+ the array will be flagged as invalid; otherwise this function
+ returns true.
+
+ \sa isValid()
+*/
+bool Q3CanvasPixmapArray::readCollisionMasks(const QString& filename)
+{
+ return readPixmaps(filename,framecount,true);
+}
+
+
+bool Q3CanvasPixmapArray::readPixmaps(const QString& datafilenamepattern,
+ int fc, bool maskonly)
+{
+ if (!maskonly) {
+ reset();
+ framecount = fc;
+ if (!framecount)
+ framecount=1;
+ img = new Q3CanvasPixmap*[framecount];
+ }
+ if (!img)
+ return false;
+
+ bool ok = true;
+ bool arg = fc > 1;
+ if (!arg)
+ framecount=1;
+ for (int i=0; i<framecount; i++) {
+ QString r;
+ r.sprintf("%04d",i);
+ if (maskonly) {
+ if (!img[i]->collision_mask)
+ img[i]->collision_mask = new QImage();
+ img[i]->collision_mask->load(
+ arg ? datafilenamepattern.arg(r) : datafilenamepattern);
+ ok = ok
+ && !img[i]->collision_mask->isNull()
+ && img[i]->collision_mask->depth()==1;
+ } else {
+ img[i]=new Q3CanvasPixmap(
+ arg ? datafilenamepattern.arg(r) : datafilenamepattern);
+ ok = ok && !img[i]->isNull();
+ }
+ }
+ if (!ok) {
+ reset();
+ }
+ return ok;
+}
+#endif
+
+/*!
+ \obsolete
+
+ Use isValid() instead.
+
+ This returns false if the array is valid, and true if it is not.
+*/
+bool Q3CanvasPixmapArray::operator!()
+{
+ return img==0;
+}
+
+/*!
+ Returns true if the pixmap array is valid; otherwise returns
+ false.
+*/
+bool Q3CanvasPixmapArray::isValid() const
+{
+ return (img != 0);
+}
+
+/*!
+ \fn Q3CanvasPixmap* Q3CanvasPixmapArray::image(int i) const
+
+ Returns pixmap \a i in the array, if \a i is non-negative and less
+ than than count(), and returns an unspecified value otherwise.
+*/
+
+// ### wouldn't it be better to put empty Q3CanvasPixmaps in there instead of
+// initializing the additional elements in the array to 0? Lars
+/*!
+ Replaces the pixmap at index \a i with pixmap \a p.
+
+ The array takes ownership of \a p and will delete \a p when the
+ array itself is deleted.
+
+ If \a i is beyond the end of the array the array is extended to at
+ least i+1 elements, with elements count() to i-1 being initialized
+ to 0.
+*/
+void Q3CanvasPixmapArray::setImage(int i, Q3CanvasPixmap* p)
+{
+ if (i >= framecount) {
+ Q3CanvasPixmap** newimg = new Q3CanvasPixmap*[i+1];
+ memcpy(newimg, img, sizeof(Q3CanvasPixmap *)*framecount);
+ memset(newimg + framecount, 0, sizeof(Q3CanvasPixmap *)*(i+1 - framecount));
+ framecount = i+1;
+ delete [] img;
+ img = newimg;
+ }
+ delete img[i]; img[i]=p;
+}
+
+/*!
+ \fn uint Q3CanvasPixmapArray::count() const
+
+ Returns the number of pixmaps in the array.
+*/
+
+/*!
+ Returns the x-coordinate of the current left edge of the sprite.
+ (This may change as the sprite animates since different frames may
+ have different left edges.)
+
+ \sa rightEdge() bottomEdge() topEdge()
+*/
+int Q3CanvasSprite::leftEdge() const
+{
+ return int(x()) - image()->hotx;
+}
+
+/*!
+ \overload
+
+ Returns what the x-coordinate of the left edge of the sprite would
+ be if the sprite (actually its hotspot) were moved to x-position
+ \a nx.
+
+ \sa rightEdge() bottomEdge() topEdge()
+*/
+int Q3CanvasSprite::leftEdge(int nx) const
+{
+ return nx - image()->hotx;
+}
+
+/*!
+ Returns the y-coordinate of the top edge of the sprite. (This may
+ change as the sprite animates since different frames may have
+ different top edges.)
+
+ \sa leftEdge() rightEdge() bottomEdge()
+*/
+int Q3CanvasSprite::topEdge() const
+{
+ return int(y()) - image()->hoty;
+}
+
+/*!
+ \overload
+
+ Returns what the y-coordinate of the top edge of the sprite would
+ be if the sprite (actually its hotspot) were moved to y-position
+ \a ny.
+
+ \sa leftEdge() rightEdge() bottomEdge()
+*/
+int Q3CanvasSprite::topEdge(int ny) const
+{
+ return ny - image()->hoty;
+}
+
+/*!
+ Returns the x-coordinate of the current right edge of the sprite.
+ (This may change as the sprite animates since different frames may
+ have different right edges.)
+
+ \sa leftEdge() bottomEdge() topEdge()
+*/
+int Q3CanvasSprite::rightEdge() const
+{
+ return leftEdge() + image()->width()-1;
+}
+
+/*!
+ \overload
+
+ Returns what the x-coordinate of the right edge of the sprite
+ would be if the sprite (actually its hotspot) were moved to
+ x-position \a nx.
+
+ \sa leftEdge() bottomEdge() topEdge()
+*/
+int Q3CanvasSprite::rightEdge(int nx) const
+{
+ return leftEdge(nx) + image()->width()-1;
+}
+
+/*!
+ Returns the y-coordinate of the current bottom edge of the sprite.
+ (This may change as the sprite animates since different frames may
+ have different bottom edges.)
+
+ \sa leftEdge() rightEdge() topEdge()
+*/
+int Q3CanvasSprite::bottomEdge() const
+{
+ return topEdge() + image()->height()-1;
+}
+
+/*!
+ \overload
+
+ Returns what the y-coordinate of the top edge of the sprite would
+ be if the sprite (actually its hotspot) were moved to y-position
+ \a ny.
+
+ \sa leftEdge() rightEdge() topEdge()
+*/
+int Q3CanvasSprite::bottomEdge(int ny) const
+{
+ return topEdge(ny) + image()->height()-1;
+}
+
+/*!
+ \fn Q3CanvasPixmap* Q3CanvasSprite::image() const
+
+ Returns the current frame's image.
+
+ \sa frame(), setFrame()
+*/
+
+/*!
+ \fn Q3CanvasPixmap* Q3CanvasSprite::image(int f) const
+ \overload
+
+ Returns the image for frame \a f. Does not do any bounds checking on \a f.
+*/
+
+/*!
+ Returns the image the sprite \e will have after advance(1) is
+ called. By default this is the same as image().
+*/
+Q3CanvasPixmap* Q3CanvasSprite::imageAdvanced() const
+{
+ return image();
+}
+
+/*!
+ Returns the bounding rectangle for the image in the sprite's
+ current frame. This assumes that the images are tightly cropped
+ (i.e. do not have transparent pixels all along a side).
+*/
+QRect Q3CanvasSprite::boundingRect() const
+{
+ return QRect(leftEdge(), topEdge(), width(), height());
+}
+
+
+/*!
+ \internal
+ Returns the chunks covered by the item.
+*/
+Q3PointArray Q3CanvasItem::chunks() const
+{
+ Q3PointArray r;
+ int n=0;
+ QRect br = boundingRect();
+ if (isVisible() && canvas()) {
+ int chunksize=canvas()->chunkSize();
+ br &= QRect(0,0,canvas()->width(),canvas()->height());
+ if (br.isValid()) {
+ r.resize((br.width()/chunksize+2)*(br.height()/chunksize+2));
+ for (int j=br.top()/chunksize; j<=br.bottom()/chunksize; j++) {
+ for (int i=br.left()/chunksize; i<=br.right()/chunksize; i++) {
+ r[n++] = QPoint(i,j);
+ }
+ }
+ }
+ }
+ r.resize(n);
+ return r;
+}
+
+
+/*!
+ \internal
+ Add the sprite to the chunks in its Q3Canvas which it overlaps.
+*/
+void Q3CanvasSprite::addToChunks()
+{
+ if (isVisible() && canvas()) {
+ int chunksize=canvas()->chunkSize();
+ for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
+ for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
+ canvas()->addItemToChunk(this,i,j);
+ }
+ }
+ }
+}
+
+/*!
+ \internal
+ Remove the sprite from the chunks in its Q3Canvas which it overlaps.
+
+ \sa addToChunks()
+*/
+void Q3CanvasSprite::removeFromChunks()
+{
+ if (isVisible() && canvas()) {
+ int chunksize=canvas()->chunkSize();
+ for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
+ for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
+ canvas()->removeItemFromChunk(this,i,j);
+ }
+ }
+ }
+}
+
+/*!
+ The width of the sprite for the current frame's image.
+
+ \sa frame()
+*/
+//### mark: Why don't we have width(int) and height(int) to be
+//consistent with leftEdge() and leftEdge(int)?
+int Q3CanvasSprite::width() const
+{
+ return image()->width();
+}
+
+/*!
+ The height of the sprite for the current frame's image.
+
+ \sa frame()
+*/
+int Q3CanvasSprite::height() const
+{
+ return image()->height();
+}
+
+
+/*!
+ Draws the current frame's image at the sprite's current position
+ on painter \a painter.
+*/
+void Q3CanvasSprite::draw(QPainter& painter)
+{
+ painter.drawPixmap(leftEdge(),topEdge(),*image());
+}
+
+/*!
+ \class Q3CanvasView
+ \compat
+ \brief The Q3CanvasView class provides an on-screen view of a Q3Canvas.
+
+ A Q3CanvasView is widget which provides a view of a Q3Canvas.
+
+ If you want users to be able to interact with a canvas view,
+ subclass Q3CanvasView. You might then reimplement
+ Q3ScrollView::contentsMousePressEvent(). For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 1
+
+ The canvas view shows canvas canvas(); this can be changed using
+ setCanvas().
+
+ A transformation matrix can be used to transform the view of the
+ canvas in various ways, for example, zooming in or out or rotating.
+ For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 2
+
+ Use setWorldMatrix() to set the canvas view's world matrix: you must
+ ensure that the world matrix is invertible. The current world matrix
+ is retrievable with worldMatrix(), and its inversion is retrievable
+ with inverseWorldMatrix().
+
+ Example:
+
+ The following code finds the part of the canvas that is visible in
+ this view, i.e. the bounding rectangle of the view in canvas coordinates.
+
+ \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 3
+
+ \sa QMatrix QPainter::setWorldMatrix(), QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Constructs a Q3CanvasView with parent \a parent, and name \a name,
+ using the widget flags \a f. The canvas view is not associated
+ with a canvas, so you must to call setCanvas() to view a
+ canvas.
+*/
+Q3CanvasView::Q3CanvasView(QWidget* parent, const char* name, Qt::WindowFlags f)
+ : Q3ScrollView(parent,name,f|WResizeNoErase|WStaticContents)
+{
+ d = new Q3CanvasViewData;
+ viewing = 0;
+ setCanvas(0);
+}
+
+/*!
+ \overload
+
+ Constructs a Q3CanvasView which views canvas \a canvas, with parent
+ \a parent, and name \a name, using the widget flags \a f.
+*/
+Q3CanvasView::Q3CanvasView(Q3Canvas* canvas, QWidget* parent, const char* name, Qt::WindowFlags f)
+ : Q3ScrollView(parent,name,f|WResizeNoErase|WStaticContents)
+{
+ d = new Q3CanvasViewData;
+ viewing = 0;
+ setCanvas(canvas);
+}
+
+/*!
+ Destroys the canvas view. The associated canvas is \e not deleted.
+*/
+Q3CanvasView::~Q3CanvasView()
+{
+ delete d;
+ d = 0;
+ setCanvas(0);
+}
+
+/*!
+ \fn Q3Canvas* Q3CanvasView::canvas() const
+
+ Returns a pointer to the canvas which the Q3CanvasView is currently
+ showing.
+*/
+
+
+/*!
+ Sets the canvas that the Q3CanvasView is showing to the canvas \a
+ canvas.
+*/
+void Q3CanvasView::setCanvas(Q3Canvas* canvas)
+{
+ if (viewing == canvas)
+ return;
+
+ if (viewing) {
+ disconnect(viewing);
+ viewing->removeView(this);
+ }
+ viewing=canvas;
+ if (viewing) {
+ connect(viewing,SIGNAL(resized()), this, SLOT(updateContentsSize()));
+ viewing->addView(this);
+ viewing->setAllChanged();
+ }
+ if (d) // called by d'tor
+ updateContentsSize();
+ update();
+}
+
+#ifndef QT_NO_TRANSFORMATIONS
+/*!
+ Returns a reference to the canvas view's current transformation matrix.
+
+ \sa setWorldMatrix() inverseWorldMatrix()
+*/
+const QMatrix &Q3CanvasView::worldMatrix() const
+{
+ return d->xform;
+}
+
+/*!
+ Returns a reference to the inverse of the canvas view's current
+ transformation matrix.
+
+ \sa setWorldMatrix() worldMatrix()
+*/
+const QMatrix &Q3CanvasView::inverseWorldMatrix() const
+{
+ return d->ixform;
+}
+
+/*!
+ Sets the transformation matrix of the Q3CanvasView to \a wm. The
+ matrix must be invertible (i.e. if you create a world matrix that
+ zooms out by 2 times, then the inverse of this matrix is one that
+ will zoom in by 2 times).
+
+ When you use this, you should note that the performance of the
+ Q3CanvasView will decrease considerably.
+
+ Returns false if \a wm is not invertable; otherwise returns true.
+
+ \sa worldMatrix() inverseWorldMatrix() QMatrix::isInvertible()
+*/
+bool Q3CanvasView::setWorldMatrix(const QMatrix & wm)
+{
+ bool ok = wm.isInvertible();
+ if (ok) {
+ d->xform = wm;
+ d->ixform = wm.invert();
+ updateContentsSize();
+ viewport()->update();
+ }
+ return ok;
+}
+#endif
+
+void Q3CanvasView::updateContentsSize()
+{
+ if (viewing) {
+ QRect br;
+#ifndef QT_NO_TRANSFORMATIONS
+ br = d->xform.map(QRect(0,0,viewing->width(),viewing->height()));
+#else
+ br = QRect(0,0,viewing->width(),viewing->height());
+#endif
+
+ if (br.width() < contentsWidth()) {
+ QRect r(contentsToViewport(QPoint(br.width(),0)),
+ QSize(contentsWidth()-br.width(),contentsHeight()));
+ d->eraseRegion = r;
+ }
+ if (br.height() < contentsHeight()) {
+ QRect r(contentsToViewport(QPoint(0,br.height())),
+ QSize(contentsWidth(),contentsHeight()-br.height()));
+ d->eraseRegion |= r;
+ }
+
+ resizeContents(br.width(),br.height());
+ } else {
+ d->eraseRegion = rect();
+ resizeContents(1,1);
+ }
+}
+
+/*!
+ Repaints part of the Q3Canvas that the canvas view is showing
+ starting at \a cx by \a cy, with a width of \a cw and a height of \a
+ ch using the painter \a p.
+*/
+void Q3CanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ QRect r(cx,cy,cw,ch);
+ if (!d->eraseRegion.isEmpty()) {
+ const QVector<QRect> rects = d->eraseRegion.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ p->eraseRect(rects.at(i));
+
+ d->eraseRegion = QRegion();
+ }
+
+ if (viewing) {
+ viewing->drawViewArea(this,p,r,false);
+ } else {
+ p->eraseRect(r);
+ }
+}
+
+/*!
+ \reimp
+ \internal
+
+ (Implemented to get rid of a compiler warning.)
+*/
+void Q3CanvasView::drawContents(QPainter *)
+{
+}
+
+/*!
+ Suggests a size sufficient to view the entire canvas.
+*/
+QSize Q3CanvasView::sizeHint() const
+{
+ if (!canvas())
+ return Q3ScrollView::sizeHint();
+ // should maybe take transformations into account
+ return (canvas()->size() + 2 * QSize(frameWidth(), frameWidth()))
+ .boundedTo(3 * QApplication::desktop()->size() / 4);
+}
+
+/*!
+ \class Q3CanvasPolygonalItem
+ \compat
+ \brief The Q3CanvasPolygonalItem class provides a polygonal canvas item
+ on a Q3Canvas.
+
+ The mostly rectangular classes, such as Q3CanvasSprite and
+ Q3CanvasText, use the object's bounding rectangle for movement,
+ repainting and collision calculations. For most other items, the
+ bounding rectangle can be far too large -- a diagonal line being
+ the worst case, and there are many other cases which are also bad.
+ Q3CanvasPolygonalItem provides polygon-based bounding rectangle
+ handling, etc., which is much faster for non-rectangular items.
+
+ Derived classes should try to define as small an area as possible
+ to maximize efficiency, but the polygon must \e definitely be
+ contained completely within the polygonal area. Calculating the
+ exact requirements is usually difficult, but if you allow a small
+ overestimate it can be easy and quick, while still getting almost
+ all of Q3CanvasPolygonalItem's speed.
+
+ Note that all subclasses \e must call hide() in their destructor
+ since hide() needs to be able to access areaPoints().
+
+ Normally, Q3CanvasPolygonalItem uses the odd-even algorithm for
+ determining whether an object intersects this object. You can
+ change this to the winding algorithm using setWinding().
+
+ The bounding rectangle is available using boundingRect(). The
+ points bounding the polygonal item are retrieved with
+ areaPoints(). Use areaPointsAdvanced() to retrieve the bounding
+ points the polygonal item \e will have after
+ Q3CanvasItem::advance(1) has been called.
+
+ If the shape of the polygonal item is about to change while the
+ item is visible, call invalidate() before updating with a
+ different result from \l areaPoints().
+
+ By default, Q3CanvasPolygonalItem objects have a black pen and no
+ brush (the default QPen and QBrush constructors). You can change
+ this with setPen() and setBrush(), but note that some
+ Q3CanvasPolygonalItem subclasses only use the brush, ignoring the
+ pen setting.
+
+ The polygonal item can be drawn on a painter with draw().
+ Subclasses must reimplement drawShape() to draw themselves.
+
+ Like any other canvas item polygonal items can be moved with
+ Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting coordinates
+ with Q3CanvasItem::setX(), Q3CanvasItem::setY() and Q3CanvasItem::setZ().
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+
+/*
+ Since most polygonal items don't have a pen, the default is
+ NoPen and a black brush.
+*/
+static const QPen& defaultPolygonPen()
+{
+ static QPen* dp=0;
+ if (!dp)
+ dp = new QPen;
+ return *dp;
+}
+
+static const QBrush& defaultPolygonBrush()
+{
+ static QBrush* db=0;
+ if (!db)
+ db = new QBrush;
+ return *db;
+}
+
+/*!
+ Constructs a Q3CanvasPolygonalItem on the canvas \a canvas.
+*/
+Q3CanvasPolygonalItem::Q3CanvasPolygonalItem(Q3Canvas* canvas) :
+ Q3CanvasItem(canvas),
+ br(defaultPolygonBrush()),
+ pn(defaultPolygonPen())
+{
+ wind=0;
+}
+
+/*!
+ Note that all subclasses \e must call hide() in their destructor
+ since hide() needs to be able to access areaPoints().
+*/
+Q3CanvasPolygonalItem::~Q3CanvasPolygonalItem()
+{
+}
+
+/*!
+ Returns true if the polygonal item uses the winding algorithm to
+ determine the "inside" of the polygon. Returns false if it uses
+ the odd-even algorithm.
+
+ The default is to use the odd-even algorithm.
+
+ \sa setWinding()
+*/
+bool Q3CanvasPolygonalItem::winding() const
+{
+ return wind;
+}
+
+/*!
+ If \a enable is true, the polygonal item will use the winding
+ algorithm to determine the "inside" of the polygon; otherwise the
+ odd-even algorithm will be used.
+
+ The default is to use the odd-even algorithm.
+
+ \sa winding()
+*/
+void Q3CanvasPolygonalItem::setWinding(bool enable)
+{
+ wind = enable;
+}
+
+/*!
+ Invalidates all information about the area covered by the canvas
+ item. The item will be updated automatically on the next call that
+ changes the item's status, for example, move() or update(). Call
+ this function if you are going to change the shape of the item (as
+ returned by areaPoints()) while the item is visible.
+*/
+void Q3CanvasPolygonalItem::invalidate()
+{
+ val = (uint)false;
+ removeFromChunks();
+}
+
+/*!
+ \fn Q3CanvasPolygonalItem::isValid() const
+
+ Returns true if the polygonal item's area information has not been
+ invalidated; otherwise returns false.
+
+ \sa invalidate()
+*/
+
+/*!
+ Returns the points the polygonal item \e will have after
+ Q3CanvasItem::advance(1) is called, i.e. what the points are when
+ advanced by the current xVelocity() and yVelocity().
+*/
+Q3PointArray Q3CanvasPolygonalItem::areaPointsAdvanced() const
+{
+ int dx = int(x()+xVelocity())-int(x());
+ int dy = int(y()+yVelocity())-int(y());
+ Q3PointArray r = areaPoints();
+ r.detach(); // Explicit sharing is stupid.
+ if (dx || dy)
+ r.translate(dx,dy);
+ return r;
+}
+
+//#define QCANVAS_POLYGONS_DEBUG
+#ifdef QCANVAS_POLYGONS_DEBUG
+static QWidget* dbg_wid=0;
+static QPainter* dbg_ptr=0;
+#endif
+
+class QPolygonalProcessor {
+public:
+ QPolygonalProcessor(Q3Canvas* c, const Q3PointArray& pa) :
+ canvas(c)
+ {
+ QRect pixelbounds = pa.boundingRect();
+ int cs = canvas->chunkSize();
+ QRect canvasbounds = pixelbounds.intersected(canvas->rect());
+ bounds.setLeft(canvasbounds.left()/cs);
+ bounds.setRight(canvasbounds.right()/cs);
+ bounds.setTop(canvasbounds.top()/cs);
+ bounds.setBottom(canvasbounds.bottom()/cs);
+ bitmap = QImage(bounds.width() + 1, bounds.height(), 1, 2, QImage::LittleEndian);
+ pnt = 0;
+ bitmap.fill(0);
+#ifdef QCANVAS_POLYGONS_DEBUG
+ dbg_start();
+#endif
+ }
+
+ inline void add(int x, int y)
+ {
+ if (pnt >= (int)result.size()) {
+ result.resize(pnt*2+10);
+ }
+ result[pnt++] = QPoint(x+bounds.x(),y+bounds.y());
+#ifdef QCANVAS_POLYGONS_DEBUG
+ if (dbg_ptr) {
+ int cs = canvas->chunkSize();
+ QRect r(x*cs+bounds.x()*cs,y*cs+bounds.y()*cs,cs-1,cs-1);
+ dbg_ptr->setPen(Qt::blue);
+ dbg_ptr->drawRect(r);
+ }
+#endif
+ }
+
+ inline void addBits(int x1, int x2, uchar newbits, int xo, int yo)
+ {
+ for (int i=x1; i<=x2; i++)
+ if (newbits & (1<<i))
+ add(xo+i,yo);
+ }
+
+#ifdef QCANVAS_POLYGONS_DEBUG
+ void dbg_start()
+ {
+ if (!dbg_wid) {
+ dbg_wid = new QWidget;
+ dbg_wid->resize(800,600);
+ dbg_wid->show();
+ dbg_ptr = new QPainter(dbg_wid);
+ dbg_ptr->setBrush(Qt::NoBrush);
+ }
+ dbg_ptr->fillRect(dbg_wid->rect(),Qt::white);
+ }
+#endif
+
+ void doSpans(int n, QPoint* pt, int* w)
+ {
+ int cs = canvas->chunkSize();
+ for (int j=0; j<n; j++) {
+ int y = pt[j].y()/cs-bounds.y();
+ if (y >= bitmap.height() || y < 0) continue;
+ uchar* l = bitmap.scanLine(y);
+ int x = pt[j].x();
+ int x1 = x/cs-bounds.x();
+ if (x1 > bounds.width()) continue;
+ x1 = QMAX(0,x1);
+ int x2 = (x+w[j])/cs-bounds.x();
+ if (x2 < 0) continue;
+ x2 = QMIN(bounds.width(), x2);
+ int x1q = x1/8;
+ int x1r = x1%8;
+ int x2q = x2/8;
+ int x2r = x2%8;
+#ifdef QCANVAS_POLYGONS_DEBUG
+ if (dbg_ptr) dbg_ptr->setPen(Qt::yellow);
+#endif
+ if (x1q == x2q) {
+ uchar newbits = (~l[x1q]) & (((2<<(x2r-x1r))-1)<<x1r);
+ if (newbits) {
+#ifdef QCANVAS_POLYGONS_DEBUG
+ if (dbg_ptr) dbg_ptr->setPen(Qt::darkGreen);
+#endif
+ addBits(x1r,x2r,newbits,x1q*8,y);
+ l[x1q] |= newbits;
+ }
+ } else {
+#ifdef QCANVAS_POLYGONS_DEBUG
+ if (dbg_ptr) dbg_ptr->setPen(Qt::blue);
+#endif
+ uchar newbits1 = (~l[x1q]) & (0xff<<x1r);
+ if (newbits1) {
+#ifdef QCANVAS_POLYGONS_DEBUG
+ if (dbg_ptr) dbg_ptr->setPen(Qt::green);
+#endif
+ addBits(x1r,7,newbits1,x1q*8,y);
+ l[x1q] |= newbits1;
+ }
+ for (int i=x1q+1; i<x2q; i++) {
+ if (l[i] != 0xff) {
+ addBits(0,7,~l[i],i*8,y);
+ l[i]=0xff;
+ }
+ }
+ uchar newbits2 = (~l[x2q]) & (0xff>>(7-x2r));
+ if (newbits2) {
+#ifdef QCANVAS_POLYGONS_DEBUG
+ if (dbg_ptr) dbg_ptr->setPen(Qt::red);
+#endif
+ addBits(0,x2r,newbits2,x2q*8,y);
+ l[x2q] |= newbits2;
+ }
+ }
+#ifdef QCANVAS_POLYGONS_DEBUG
+ if (dbg_ptr) {
+ dbg_ptr->drawLine(pt[j],pt[j]+QPoint(w[j],0));
+ }
+#endif
+ }
+ result.resize(pnt);
+ }
+
+ int pnt;
+ Q3PointArray result;
+ Q3Canvas* canvas;
+ QRect bounds;
+ QImage bitmap;
+};
+
+
+Q3PointArray Q3CanvasPolygonalItem::chunks() const
+{
+ Q3PointArray pa = areaPoints();
+
+ if (!pa.size()) {
+ pa.detach(); // Explicit sharing is stupid.
+ return pa;
+ }
+
+ QPolygonalProcessor processor(canvas(),pa);
+
+ scanPolygon(pa, wind, processor);
+
+ return processor.result;
+}
+/*!
+ Simply calls Q3CanvasItem::chunks().
+*/
+Q3PointArray Q3CanvasRectangle::chunks() const
+{
+ // No need to do a polygon scan!
+ return Q3CanvasItem::chunks();
+}
+
+/*!
+ Returns the bounding rectangle of the polygonal item, based on
+ areaPoints().
+*/
+QRect Q3CanvasPolygonalItem::boundingRect() const
+{
+ return areaPoints().boundingRect();
+}
+
+/*!
+ Reimplemented from Q3CanvasItem, this draws the polygonal item by
+ setting the pen and brush for the item on the painter \a p and
+ calling drawShape().
+*/
+void Q3CanvasPolygonalItem::draw(QPainter & p)
+{
+ p.setPen(pn);
+ p.setBrush(br);
+ drawShape(p);
+}
+
+/*!
+ \fn void Q3CanvasPolygonalItem::drawShape(QPainter & p)
+
+ Subclasses must reimplement this function to draw their shape. The
+ pen and brush of \a p are already set to pen() and brush() prior
+ to calling this function.
+
+ \sa draw()
+*/
+
+/*!
+ \fn QPen Q3CanvasPolygonalItem::pen() const
+
+ Returns the QPen used to draw the outline of the item, if any.
+
+ \sa setPen()
+*/
+
+/*!
+ \fn QBrush Q3CanvasPolygonalItem::brush() const
+
+ Returns the QBrush used to fill the item, if filled.
+
+ \sa setBrush()
+*/
+
+/*!
+ Sets the QPen used when drawing the item to the pen \a p.
+ Note that many Q3CanvasPolygonalItems do not use the pen value.
+
+ \sa setBrush(), pen(), drawShape()
+*/
+void Q3CanvasPolygonalItem::setPen(QPen p)
+{
+ if (pn != p) {
+ removeFromChunks();
+ pn = p;
+ addToChunks();
+ }
+}
+
+/*!
+ Sets the QBrush used when drawing the polygonal item to the brush \a b.
+
+ \sa setPen(), brush(), drawShape()
+*/
+void Q3CanvasPolygonalItem::setBrush(QBrush b)
+{
+ if (br != b) {
+ br = b;
+ changeChunks();
+ }
+}
+
+
+/*!
+ \class Q3CanvasPolygon
+ \compat
+ \brief The Q3CanvasPolygon class provides a polygon on a Q3Canvas.
+
+ Paints a polygon with a QBrush. The polygon's points can be set in
+ the constructor or set or changed later using setPoints(). Use
+ points() to retrieve the points, or areaPoints() to retrieve the
+ points relative to the canvas's origin.
+
+ The polygon can be drawn on a painter with drawShape().
+
+ Like any other canvas item polygons can be moved with
+ Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
+ coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
+ Q3CanvasItem::setZ().
+
+ Note: Q3CanvasPolygon does not use the pen.
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Constructs a point-less polygon on the canvas \a canvas. You
+ should call setPoints() before using it further.
+*/
+Q3CanvasPolygon::Q3CanvasPolygon(Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas)
+{
+}
+
+/*!
+ Destroys the polygon.
+*/
+Q3CanvasPolygon::~Q3CanvasPolygon()
+{
+ hide();
+}
+
+/*!
+ Draws the polygon using the painter \a p.
+
+ Note that Q3CanvasPolygon does not support an outline (the pen is
+ always NoPen).
+*/
+void Q3CanvasPolygon::drawShape(QPainter & p)
+{
+ // ### why can't we draw outlines? We could use drawPolyline for it. Lars
+ // ### see other message. Warwick
+
+ p.setPen(NoPen); // since QRegion(Q3PointArray) excludes outline :-()-:
+ p.drawPolygon(poly);
+}
+
+/*!
+ Sets the points of the polygon to be \a pa. These points will have
+ their x and y coordinates automatically translated by x(), y() as
+ the polygon is moved.
+*/
+void Q3CanvasPolygon::setPoints(Q3PointArray pa)
+{
+ removeFromChunks();
+ poly = pa;
+ poly.detach(); // Explicit sharing is stupid.
+ poly.translate((int)x(),(int)y());
+ addToChunks();
+}
+
+/*!
+ \reimp
+*/
+void Q3CanvasPolygon::moveBy(double dx, double dy)
+{
+ // Note: does NOT call Q3CanvasPolygonalItem::moveBy(), since that
+ // only does half this work.
+ //
+ int idx = int(x()+dx)-int(x());
+ int idy = int(y()+dy)-int(y());
+ if (idx || idy) {
+ removeFromChunks();
+ poly.translate(idx,idy);
+ }
+ myx+=dx;
+ myy+=dy;
+ if (idx || idy) {
+ addToChunks();
+ }
+}
+
+/*!
+ \class Q3CanvasSpline
+ \compat
+ \brief The Q3CanvasSpline class provides multi-bezier splines on a Q3Canvas.
+
+ A Q3CanvasSpline is a sequence of 4-point bezier curves joined
+ together to make a curved shape.
+
+ You set the control points of the spline with setControlPoints().
+
+ If the bezier is closed(), then the first control point will be
+ re-used as the last control point. Therefore, a closed bezier must
+ have a multiple of 3 control points and an open bezier must have
+ one extra point.
+
+ The beziers are not necessarily joined "smoothly". To ensure this,
+ set control points appropriately (general reference texts about
+ beziers will explain this in detail).
+
+ Like any other canvas item splines can be moved with
+ Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
+ coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
+ Q3CanvasItem::setZ().
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Create a spline with no control points on the canvas \a canvas.
+
+ \sa setControlPoints()
+*/
+Q3CanvasSpline::Q3CanvasSpline(Q3Canvas* canvas) :
+ Q3CanvasPolygon(canvas),
+ cl(true)
+{
+}
+
+/*!
+ Destroy the spline.
+*/
+Q3CanvasSpline::~Q3CanvasSpline()
+{
+}
+
+/*!
+ Set the spline control points to \a ctrl.
+
+ If \a close is true, then the first point in \a ctrl will be
+ re-used as the last point, and the number of control points must
+ be a multiple of 3. If \a close is false, one additional control
+ point is required, and the number of control points must be one of
+ (4, 7, 10, 13, ...).
+
+ If the number of control points doesn't meet the above conditions,
+ the number of points will be truncated to the largest number of
+ points that do meet the requirement.
+*/
+void Q3CanvasSpline::setControlPoints(Q3PointArray ctrl, bool close)
+{
+ if ((int)ctrl.count() % 3 != (close ? 0 : 1)) {
+ qWarning("Q3CanvasSpline::setControlPoints(): Number of points doesn't fit.");
+ int numCurves = (ctrl.count() - (close ? 0 : 1))/ 3;
+ ctrl.resize(numCurves*3 + (close ? 0 : 1));
+ }
+
+ cl = close;
+ bez = ctrl;
+ recalcPoly();
+}
+
+/*!
+ Returns the current set of control points.
+
+ \sa setControlPoints(), closed()
+*/
+Q3PointArray Q3CanvasSpline::controlPoints() const
+{
+ return bez;
+}
+
+/*!
+ Returns true if the control points are a closed set; otherwise
+ returns false.
+*/
+bool Q3CanvasSpline::closed() const
+{
+ return cl;
+}
+
+void Q3CanvasSpline::recalcPoly()
+{
+ Q3PtrList<Q3PointArray> segs;
+ segs.setAutoDelete(true);
+ int n=0;
+ for (int i=0; i<(int)bez.count()-1; i+=3) {
+ Q3PointArray ctrl(4);
+ ctrl[0] = bez[i+0];
+ ctrl[1] = bez[i+1];
+ ctrl[2] = bez[i+2];
+ if (cl)
+ ctrl[3] = bez[(i+3)%(int)bez.count()];
+ else
+ ctrl[3] = bez[i+3];
+ Q3PointArray *seg = new Q3PointArray(ctrl.cubicBezier());
+ n += seg->count()-1;
+ segs.append(seg);
+ }
+ Q3PointArray p(n+1);
+ n=0;
+ for (Q3PointArray* seg = segs.first(); seg; seg = segs.next()) {
+ for (int i=0; i<(int)seg->count()-1; i++)
+ p[n++] = seg->point(i);
+ if (n == (int)p.count()-1)
+ p[n] = seg->point(seg->count()-1);
+ }
+ Q3CanvasPolygon::setPoints(p);
+}
+
+/*!
+ \fn Q3PointArray Q3CanvasPolygonalItem::areaPoints() const
+
+ This function must be reimplemented by subclasses. It \e must
+ return the points bounding (i.e. outside and not touching) the
+ shape or drawing errors will occur.
+*/
+
+/*!
+ \fn Q3PointArray Q3CanvasPolygon::points() const
+
+ Returns the vertices of the polygon, not translated by the position.
+
+ \sa setPoints(), areaPoints()
+*/
+Q3PointArray Q3CanvasPolygon::points() const
+{
+ Q3PointArray pa = areaPoints();
+ pa.translate(int(-x()),int(-y()));
+ return pa;
+}
+
+/*!
+ Returns the vertices of the polygon translated by the polygon's
+ current x(), y() position, i.e. relative to the canvas's origin.
+
+ \sa setPoints(), points()
+*/
+Q3PointArray Q3CanvasPolygon::areaPoints() const
+{
+ return poly.copy();
+}
+
+/*!
+ \class Q3CanvasLine
+ \compat
+ \brief The Q3CanvasLine class provides a line on a Q3Canvas.
+
+ The line inherits functionality from Q3CanvasPolygonalItem, for
+ example the setPen() function. The start and end points of the
+ line are set with setPoints().
+
+ Like any other canvas item lines can be moved with
+ Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
+ coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
+ Q3CanvasItem::setZ().
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Constructs a line from (0,0) to (0,0) on \a canvas.
+
+ \sa setPoints()
+*/
+Q3CanvasLine::Q3CanvasLine(Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas)
+{
+ x1 = y1 = x2 = y2 = 0;
+}
+
+/*!
+ Destroys the line.
+*/
+Q3CanvasLine::~Q3CanvasLine()
+{
+ hide();
+}
+
+/*!
+ \reimp
+*/
+void Q3CanvasLine::setPen(QPen p)
+{
+ Q3CanvasPolygonalItem::setPen(p);
+}
+
+/*!
+ \fn QPoint Q3CanvasLine::startPoint () const
+
+ Returns the start point of the line.
+
+ \sa setPoints(), endPoint()
+*/
+
+/*!
+ \fn QPoint Q3CanvasLine::endPoint () const
+
+ Returns the end point of the line.
+
+ \sa setPoints(), startPoint()
+*/
+
+/*!
+ Sets the line's start point to (\a xa, \a ya) and its end point to
+ (\a xb, \a yb).
+*/
+void Q3CanvasLine::setPoints(int xa, int ya, int xb, int yb)
+{
+ if (x1 != xa || x2 != xb || y1 != ya || y2 != yb) {
+ removeFromChunks();
+ x1 = xa;
+ y1 = ya;
+ x2 = xb;
+ y2 = yb;
+ addToChunks();
+ }
+}
+
+/*!
+ \reimp
+*/
+void Q3CanvasLine::drawShape(QPainter &p)
+{
+ p.drawLine((int)(x()+x1), (int)(y()+y1), (int)(x()+x2), (int)(y()+y2));
+}
+
+/*!
+ \reimp
+
+ Note that the area defined by the line is somewhat thicker than
+ the line that is actually drawn.
+*/
+Q3PointArray Q3CanvasLine::areaPoints() const
+{
+ Q3PointArray p(4);
+ int xi = int(x());
+ int yi = int(y());
+ int pw = pen().width();
+ int dx = QABS(x1-x2);
+ int dy = QABS(y1-y2);
+ pw = pw*4/3+2; // approx pw*sqrt(2)
+ int px = x1<x2 ? -pw : pw ;
+ int py = y1<y2 ? -pw : pw ;
+ if (dx && dy && (dx > dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2))) {
+ // steep
+ if (px == py) {
+ p[0] = QPoint(x1+xi ,y1+yi+py);
+ p[1] = QPoint(x2+xi-px,y2+yi );
+ p[2] = QPoint(x2+xi ,y2+yi-py);
+ p[3] = QPoint(x1+xi+px,y1+yi );
+ } else {
+ p[0] = QPoint(x1+xi+px,y1+yi );
+ p[1] = QPoint(x2+xi ,y2+yi-py);
+ p[2] = QPoint(x2+xi-px,y2+yi );
+ p[3] = QPoint(x1+xi ,y1+yi+py);
+ }
+ } else if (dx > dy) {
+ // horizontal
+ p[0] = QPoint(x1+xi+px,y1+yi+py);
+ p[1] = QPoint(x2+xi-px,y2+yi+py);
+ p[2] = QPoint(x2+xi-px,y2+yi-py);
+ p[3] = QPoint(x1+xi+px,y1+yi-py);
+ } else {
+ // vertical
+ p[0] = QPoint(x1+xi+px,y1+yi+py);
+ p[1] = QPoint(x2+xi+px,y2+yi-py);
+ p[2] = QPoint(x2+xi-px,y2+yi-py);
+ p[3] = QPoint(x1+xi-px,y1+yi+py);
+ }
+ return p;
+}
+
+/*!
+ \reimp
+
+*/
+
+void Q3CanvasLine::moveBy(double dx, double dy)
+{
+ Q3CanvasPolygonalItem::moveBy(dx, dy);
+}
+
+/*!
+ \class Q3CanvasRectangle
+ \compat
+ \brief The Q3CanvasRectangle class provides a rectangle on a Q3Canvas.
+
+ This item paints a single rectangle which may have any pen() and
+ brush(), but may not be tilted/rotated. For rotated rectangles,
+ use Q3CanvasPolygon.
+
+ The rectangle's size and initial position can be set in the
+ constructor. The size can be set or changed later using setSize().
+ Use height() and width() to retrieve the rectangle's dimensions.
+
+ The rectangle can be drawn on a painter with drawShape().
+
+ Like any other canvas item rectangles can be moved with
+ Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
+ coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
+ Q3CanvasItem::setZ().
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Constructs a rectangle at position (0,0) with both width and
+ height set to 32 pixels on \a canvas.
+*/
+Q3CanvasRectangle::Q3CanvasRectangle(Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas),
+ w(32), h(32)
+{
+}
+
+/*!
+ Constructs a rectangle positioned and sized by \a r on \a canvas.
+*/
+Q3CanvasRectangle::Q3CanvasRectangle(const QRect& r, Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas),
+ w(r.width()), h(r.height())
+{
+ move(r.x(),r.y());
+}
+
+/*!
+ Constructs a rectangle at position (\a x, \a y) and size \a width
+ by \a height, on \a canvas.
+*/
+Q3CanvasRectangle::Q3CanvasRectangle(int x, int y, int width, int height,
+ Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas),
+ w(width), h(height)
+{
+ move(x,y);
+}
+
+/*!
+ Destroys the rectangle.
+*/
+Q3CanvasRectangle::~Q3CanvasRectangle()
+{
+ hide();
+}
+
+
+/*!
+ Returns the width of the rectangle.
+*/
+int Q3CanvasRectangle::width() const
+{
+ return w;
+}
+
+/*!
+ Returns the height of the rectangle.
+*/
+int Q3CanvasRectangle::height() const
+{
+ return h;
+}
+
+/*!
+ Sets the \a width and \a height of the rectangle.
+*/
+void Q3CanvasRectangle::setSize(int width, int height)
+{
+ if (w != width || h != height) {
+ removeFromChunks();
+ w = width;
+ h = height;
+ addToChunks();
+ }
+}
+
+/*!
+ \fn QSize Q3CanvasRectangle::size() const
+
+ Returns the width() and height() of the rectangle.
+
+ \sa rect(), setSize()
+*/
+
+/*!
+ \fn QRect Q3CanvasRectangle::rect() const
+
+ Returns the integer-converted x(), y() position and size() of the
+ rectangle as a QRect.
+*/
+
+/*!
+ \reimp
+*/
+Q3PointArray Q3CanvasRectangle::areaPoints() const
+{
+ Q3PointArray pa(4);
+ int pw = (pen().width()+1)/2;
+ if (pw < 1) pw = 1;
+ if (pen() == NoPen) pw = 0;
+ pa[0] = QPoint((int)x()-pw,(int)y()-pw);
+ pa[1] = pa[0] + QPoint(w+pw*2,0);
+ pa[2] = pa[1] + QPoint(0,h+pw*2);
+ pa[3] = pa[0] + QPoint(0,h+pw*2);
+ return pa;
+}
+
+/*!
+ Draws the rectangle on painter \a p.
+*/
+void Q3CanvasRectangle::drawShape(QPainter & p)
+{
+ p.drawRect((int)x(), (int)y(), w, h);
+}
+
+
+/*!
+ \class Q3CanvasEllipse
+ \compat
+ \brief The Q3CanvasEllipse class provides an ellipse or ellipse segment on a Q3Canvas.
+
+ A canvas item that paints an ellipse or ellipse segment with a QBrush.
+ The ellipse's height, width, start angle and angle length can be set
+ at construction time. The size can be changed at runtime with
+ setSize(), and the angles can be changed (if you're displaying an
+ ellipse segment rather than a whole ellipse) with setAngles().
+
+ Note that angles are specified in 16ths of a degree.
+
+ \target anglediagram
+ \img qcanvasellipse.png Ellipse
+
+ If a start angle and length angle are set then an ellipse segment
+ will be drawn. The start angle is the angle that goes from zero in a
+ counter-clockwise direction (shown in green in the diagram). The
+ length angle is the angle from the start angle in a
+ counter-clockwise direction (shown in blue in the diagram). The blue
+ segment is the segment of the ellipse that would be drawn. If no
+ start angle and length angle are specified the entire ellipse is
+ drawn.
+
+ The ellipse can be drawn on a painter with drawShape().
+
+ Like any other canvas item ellipses can be moved with move() and
+ moveBy(), or by setting coordinates with setX(), setY() and setZ().
+
+ Note: Q3CanvasEllipse does not use the pen.
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Constructs a 32x32 ellipse, centered at (0, 0) on \a canvas.
+*/
+Q3CanvasEllipse::Q3CanvasEllipse(Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas),
+ w(32), h(32),
+ a1(0), a2(360*16)
+{
+}
+
+/*!
+ Constructs a \a width by \a height pixel ellipse, centered at
+ (0, 0) on \a canvas.
+*/
+Q3CanvasEllipse::Q3CanvasEllipse(int width, int height, Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas),
+ w(width),h(height),
+ a1(0),a2(360*16)
+{
+}
+
+// ### add a constructor taking degrees in float. 1/16 degrees is stupid. Lars
+// ### it's how QPainter does it, so Q3Canvas does too for consistency. If it's
+// ### a good idea, it should be added to QPainter, not just to Q3Canvas. Warwick
+/*!
+ Constructs a \a width by \a height pixel ellipse, centered at
+ (0, 0) on \a canvas. Only a segment of the ellipse is drawn,
+ starting at angle \a startangle, and extending for angle \a angle
+ (the angle length).
+
+ Note that angles are specified in sixteenths of a degree.
+*/
+Q3CanvasEllipse::Q3CanvasEllipse(int width, int height,
+ int startangle, int angle, Q3Canvas* canvas) :
+ Q3CanvasPolygonalItem(canvas),
+ w(width),h(height),
+ a1(startangle),a2(angle)
+{
+}
+
+/*!
+ Destroys the ellipse.
+*/
+Q3CanvasEllipse::~Q3CanvasEllipse()
+{
+ hide();
+}
+
+/*!
+ Returns the width of the ellipse.
+*/
+int Q3CanvasEllipse::width() const
+{
+ return w;
+}
+
+/*!
+ Returns the height of the ellipse.
+*/
+int Q3CanvasEllipse::height() const
+{
+ return h;
+}
+
+/*!
+ Sets the \a width and \a height of the ellipse.
+*/
+void Q3CanvasEllipse::setSize(int width, int height)
+{
+ if (w != width || h != height) {
+ removeFromChunks();
+ w = width;
+ h = height;
+ addToChunks();
+ }
+}
+
+/*!
+ \fn int Q3CanvasEllipse::angleStart() const
+
+ Returns the start angle in 16ths of a degree. Initially
+ this will be 0.
+
+ \sa setAngles(), angleLength()
+*/
+
+/*!
+ \fn int Q3CanvasEllipse::angleLength() const
+
+ Returns the length angle (the extent of the ellipse segment) in
+ 16ths of a degree. Initially this will be 360 * 16 (a complete
+ ellipse).
+
+ \sa setAngles(), angleStart()
+*/
+
+/*!
+ Sets the angles for the ellipse. The start angle is \a start and
+ the extent of the segment is \a length (the angle length) from the
+ \a start. The angles are specified in 16ths of a degree. By
+ default the ellipse will start at 0 and have an angle length of
+ 360 * 16 (a complete ellipse).
+
+ \sa angleStart(), angleLength()
+*/
+void Q3CanvasEllipse::setAngles(int start, int length)
+{
+ if (a1 != start || a2 != length) {
+ removeFromChunks();
+ a1 = start;
+ a2 = length;
+ addToChunks();
+ }
+}
+
+/*!
+ \reimp
+*/
+Q3PointArray Q3CanvasEllipse::areaPoints() const
+{
+ Q3PointArray r;
+ // makeArc at 0,0, then translate so that fixed point math doesn't overflow
+ r.makeArc(int(x()-w/2.0+0.5)-1, int(y()-h/2.0+0.5)-1, w+3, h+3, a1, a2);
+ r.resize(r.size()+1);
+ r.setPoint(r.size()-1,int(x()),int(y()));
+ return r;
+}
+
+// ### support outlines! Lars
+// ### QRegion doesn't, so we cannot (try it). Warwick
+/*!
+ Draws the ellipse, centered at x(), y() using the painter \a p.
+
+ Note that Q3CanvasEllipse does not support an outline (the pen is
+ always NoPen).
+*/
+void Q3CanvasEllipse::drawShape(QPainter & p)
+{
+ p.setPen(NoPen); // since QRegion(Q3PointArray) excludes outline :-()-:
+ if (!a1 && a2 == 360*16) {
+ p.drawEllipse(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h);
+ } else {
+ p.drawPie(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h, a1, a2);
+ }
+}
+
+
+/*!
+ \class Q3CanvasText
+ \compat
+ \brief The Q3CanvasText class provides a text object on a Q3Canvas.
+
+ A canvas text item has text with font, color and alignment
+ attributes. The text and font can be set in the constructor or set
+ or changed later with setText() and setFont(). The color is set
+ with setColor() and the alignment with setTextFlags(). The text
+ item's bounding rectangle is retrieved with boundingRect().
+
+ The text can be drawn on a painter with draw().
+
+ Like any other canvas item text items can be moved with
+ Q3CanvasItem::move() and Q3CanvasItem::moveBy(), or by setting
+ coordinates with Q3CanvasItem::setX(), Q3CanvasItem::setY() and
+ Q3CanvasItem::setZ().
+
+ \sa QtCanvas, {Porting to Graphics View}
+*/
+
+/*!
+ Constructs a Q3CanvasText with the text "\<text\>", on \a canvas.
+*/
+Q3CanvasText::Q3CanvasText(Q3Canvas* canvas) :
+ Q3CanvasItem(canvas),
+ txt(QLatin1String("<text>")), flags(0)
+{
+ setRect();
+}
+
+// ### add textflags to the constructor? Lars
+/*!
+ Constructs a Q3CanvasText with the text \a t, on canvas \a canvas.
+*/
+Q3CanvasText::Q3CanvasText(const QString& t, Q3Canvas* canvas) :
+ Q3CanvasItem(canvas),
+ txt(t), flags(0)
+{
+ setRect();
+}
+
+// ### see above
+/*!
+ Constructs a Q3CanvasText with the text \a t and font \a f, on the
+ canvas \a canvas.
+*/
+Q3CanvasText::Q3CanvasText(const QString& t, QFont f, Q3Canvas* canvas) :
+ Q3CanvasItem(canvas),
+ txt(t), flags(0),
+ fnt(f)
+{
+ setRect();
+}
+
+/*!
+ Destroys the canvas text item.
+*/
+Q3CanvasText::~Q3CanvasText()
+{
+ removeFromChunks();
+}
+
+/*!
+ Returns the bounding rectangle of the text.
+*/
+QRect Q3CanvasText::boundingRect() const { return brect; }
+
+void Q3CanvasText::setRect()
+{
+ brect = QFontMetrics(fnt).boundingRect(int(x()), int(y()), 0, 0, flags, txt);
+}
+
+/*!
+ \fn int Q3CanvasText::textFlags() const
+
+ Returns the currently set alignment flags.
+
+ \sa setTextFlags() Qt::AlignmentFlag
+*/
+
+
+/*!
+ Sets the alignment flags to \a f. These are a bitwise OR of the
+ flags available to QPainter::drawText() -- see the
+ \l{Qt::AlignmentFlag}s.
+
+ \sa setFont() setColor()
+*/
+void Q3CanvasText::setTextFlags(int f)
+{
+ if (flags != f) {
+ removeFromChunks();
+ flags = f;
+ setRect();
+ addToChunks();
+ }
+}
+
+/*!
+ Returns the text item's text.
+
+ \sa setText()
+*/
+QString Q3CanvasText::text() const
+{
+ return txt;
+}
+
+
+/*!
+ Sets the text item's text to \a t. The text may contain newlines.
+
+ \sa text(), setFont(), setColor() setTextFlags()
+*/
+void Q3CanvasText::setText(const QString& t)
+{
+ if (txt != t) {
+ removeFromChunks();
+ txt = t;
+ setRect();
+ addToChunks();
+ }
+}
+
+/*!
+ Returns the font in which the text is drawn.
+
+ \sa setFont()
+*/
+QFont Q3CanvasText::font() const
+{
+ return fnt;
+}
+
+/*!
+ Sets the font in which the text is drawn to font \a f.
+
+ \sa font()
+*/
+void Q3CanvasText::setFont(const QFont& f)
+{
+ if (f != fnt) {
+ removeFromChunks();
+ fnt = f;
+ setRect();
+ addToChunks();
+ }
+}
+
+/*!
+ Returns the color of the text.
+
+ \sa setColor()
+*/
+QColor Q3CanvasText::color() const
+{
+ return col;
+}
+
+/*!
+ Sets the color of the text to the color \a c.
+
+ \sa color(), setFont()
+*/
+void Q3CanvasText::setColor(const QColor& c)
+{
+ col=c;
+ changeChunks();
+}
+
+
+/*!
+ \reimp
+*/
+void Q3CanvasText::moveBy(double dx, double dy)
+{
+ int idx = int(x()+dx)-int(x());
+ int idy = int(y()+dy)-int(y());
+ if (idx || idy) {
+ removeFromChunks();
+ }
+ myx+=dx;
+ myy+=dy;
+ if (idx || idy) {
+ brect.moveBy(idx,idy);
+ addToChunks();
+ }
+}
+
+/*!
+ Draws the text using the painter \a painter.
+*/
+void Q3CanvasText::draw(QPainter& painter)
+{
+ painter.setFont(fnt);
+ painter.setPen(col);
+ painter.drawText(painter.fontMetrics().boundingRect(int(x()), int(y()), 0, 0, flags, txt), flags, txt);
+}
+
+/*!
+ \internal
+*/
+void Q3CanvasText::changeChunks()
+{
+ if (isVisible() && canvas()) {
+ int chunksize=canvas()->chunkSize();
+ for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
+ for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
+ canvas()->setChangedChunk(i,j);
+ }
+ }
+ }
+}
+
+/*!
+ Adds the text item to the appropriate chunks.
+*/
+void Q3CanvasText::addToChunks()
+{
+ if (isVisible() && canvas()) {
+ int chunksize=canvas()->chunkSize();
+ for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
+ for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
+ canvas()->addItemToChunk(this,i,j);
+ }
+ }
+ }
+}
+
+/*!
+ Removes the text item from the appropriate chunks.
+*/
+void Q3CanvasText::removeFromChunks()
+{
+ if (isVisible() && canvas()) {
+ int chunksize=canvas()->chunkSize();
+ for (int j=brect.top()/chunksize; j<=brect.bottom()/chunksize; j++) {
+ for (int i=brect.left()/chunksize; i<=brect.right()/chunksize; i++) {
+ canvas()->removeItemFromChunk(this,i,j);
+ }
+ }
+ }
+}
+
+
+/*!
+ Returns 0 (Q3CanvasItem::Rtti_Item).
+
+ Make your derived classes return their own values for rtti(), so
+ that you can distinguish between objects returned by
+ Q3Canvas::at(). You should use values greater than 1000 to allow
+ for extensions to this class.
+
+ Overuse of this functionality can damage its extensibility. For
+ example, once you have identified a base class of a Q3CanvasItem
+ found by Q3Canvas::at(), cast it to that type and call meaningful
+ methods rather than acting upon the object based on its rtti
+ value.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_canvas_q3canvas.cpp 4
+*/
+int Q3CanvasItem::rtti() const { return RTTI; }
+int Q3CanvasItem::RTTI = Rtti_Item;
+
+/*!
+ Returns 1 (Q3CanvasItem::Rtti_Sprite).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasSprite::rtti() const { return RTTI; }
+int Q3CanvasSprite::RTTI = Rtti_Sprite;
+
+/*!
+ Returns 2 (Q3CanvasItem::Rtti_PolygonalItem).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasPolygonalItem::rtti() const { return RTTI; }
+int Q3CanvasPolygonalItem::RTTI = Rtti_PolygonalItem;
+
+/*!
+ Returns 3 (Q3CanvasItem::Rtti_Text).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasText::rtti() const { return RTTI; }
+int Q3CanvasText::RTTI = Rtti_Text;
+
+/*!
+ Returns 4 (Q3CanvasItem::Rtti_Polygon).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasPolygon::rtti() const { return RTTI; }
+int Q3CanvasPolygon::RTTI = Rtti_Polygon;
+
+/*!
+ Returns 5 (Q3CanvasItem::Rtti_Rectangle).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasRectangle::rtti() const { return RTTI; }
+int Q3CanvasRectangle::RTTI = Rtti_Rectangle;
+
+/*!
+ Returns 6 (Q3CanvasItem::Rtti_Ellipse).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasEllipse::rtti() const { return RTTI; }
+int Q3CanvasEllipse::RTTI = Rtti_Ellipse;
+
+/*!
+ Returns 7 (Q3CanvasItem::Rtti_Line).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasLine::rtti() const { return RTTI; }
+int Q3CanvasLine::RTTI = Rtti_Line;
+
+/*!
+ Returns 8 (Q3CanvasItem::Rtti_Spline).
+
+ \sa Q3CanvasItem::rtti()
+*/
+int Q3CanvasSpline::rtti() const { return RTTI; }
+int Q3CanvasSpline::RTTI = Rtti_Spline;
+
+/*!
+ Constructs a Q3CanvasSprite which uses images from the
+ Q3CanvasPixmapArray \a a.
+
+ The sprite in initially positioned at (0, 0) on \a canvas, using
+ frame 0.
+*/
+Q3CanvasSprite::Q3CanvasSprite(Q3CanvasPixmapArray* a, Q3Canvas* canvas) :
+ Q3CanvasItem(canvas),
+ frm(0),
+ anim_val(0),
+ anim_state(0),
+ anim_type(0),
+ images(a)
+{
+}
+
+
+/*!
+ Set the array of images used for displaying the sprite to the
+ Q3CanvasPixmapArray \a a.
+
+ If the current frame() is larger than the number of images in \a
+ a, the current frame will be reset to 0.
+*/
+void Q3CanvasSprite::setSequence(Q3CanvasPixmapArray* a)
+{
+ bool isvisible = isVisible();
+ if (isvisible && images)
+ hide();
+ images = a;
+ if (frm >= (int)images->count())
+ frm = 0;
+ if (isvisible)
+ show();
+}
+
+/*!
+\internal
+
+Marks any chunks the sprite touches as changed.
+*/
+void Q3CanvasSprite::changeChunks()
+{
+ if (isVisible() && canvas()) {
+ int chunksize=canvas()->chunkSize();
+ for (int j=topEdge()/chunksize; j<=bottomEdge()/chunksize; j++) {
+ for (int i=leftEdge()/chunksize; i<=rightEdge()/chunksize; i++) {
+ canvas()->setChangedChunk(i,j);
+ }
+ }
+ }
+}
+
+/*!
+ Destroys the sprite and removes it from the canvas. Does \e not
+ delete the images.
+*/
+Q3CanvasSprite::~Q3CanvasSprite()
+{
+ removeFromChunks();
+}
+
+/*!
+ Sets the animation frame used for displaying the sprite to \a f,
+ an index into the Q3CanvasSprite's Q3CanvasPixmapArray. The call
+ will be ignored if \a f is larger than frameCount() or smaller
+ than 0.
+
+ \sa frame() move()
+*/
+void Q3CanvasSprite::setFrame(int f)
+{
+ move(x(),y(),f);
+}
+
+/*!
+ \enum Q3CanvasSprite::FrameAnimationType
+
+ This enum is used to identify the different types of frame
+ animation offered by Q3CanvasSprite.
+
+ \value Cycle at each advance the frame number will be incremented by
+ 1 (modulo the frame count).
+ \value Oscillate at each advance the frame number will be
+ incremented by 1 up to the frame count then decremented to by 1 to
+ 0, repeating this sequence forever.
+*/
+
+/*!
+ Sets the animation characteristics for the sprite.
+
+ For \a type == \c Cycle, the frames will increase by \a step
+ at each advance, modulo the frameCount().
+
+ For \a type == \c Oscillate, the frames will increase by \a step
+ at each advance, up to the frameCount(), then decrease by \a step
+ back to 0, repeating forever.
+
+ The \a state parameter is for internal use.
+*/
+void Q3CanvasSprite::setFrameAnimation(FrameAnimationType type, int step, int state)
+{
+ anim_val = step;
+ anim_type = type;
+ anim_state = state;
+ setAnimated(true);
+}
+
+/*!
+ Extends the default Q3CanvasItem implementation to provide the
+ functionality of setFrameAnimation().
+
+ The \a phase is 0 or 1: see Q3CanvasItem::advance() for details.
+
+ \sa Q3CanvasItem::advance() setVelocity()
+*/
+void Q3CanvasSprite::advance(int phase)
+{
+ if (phase==1) {
+ int nf = frame();
+ if (anim_type == Oscillate) {
+ if (anim_state)
+ nf += anim_val;
+ else
+ nf -= anim_val;
+ if (nf < 0) {
+ nf = abs(anim_val);
+ anim_state = !anim_state;
+ } else if (nf >= frameCount()) {
+ nf = frameCount()-1-abs(anim_val);
+ anim_state = !anim_state;
+ }
+ } else {
+ nf = (nf + anim_val + frameCount()) % frameCount();
+ }
+ move(x()+xVelocity(),y()+yVelocity(),nf);
+ }
+}
+
+
+/*!
+ \fn int Q3CanvasSprite::frame() const
+
+ Returns the index of the current animation frame in the
+ Q3CanvasSprite's Q3CanvasPixmapArray.
+
+ \sa setFrame(), move()
+*/
+
+/*!
+ \fn int Q3CanvasSprite::frameCount() const
+
+ Returns the number of frames in the Q3CanvasSprite's
+ Q3CanvasPixmapArray.
+*/
+
+
+/*!
+ Moves the sprite to (\a x, \a y).
+*/
+void Q3CanvasSprite::move(double x, double y) { Q3CanvasItem::move(x,y); }
+
+/*!
+ \fn void Q3CanvasSprite::move(double nx, double ny, int nf)
+
+ Moves the sprite to (\a nx, \a ny) and sets the current
+ frame to \a nf. \a nf will be ignored if it is larger than
+ frameCount() or smaller than 0.
+*/
+void Q3CanvasSprite::move(double nx, double ny, int nf)
+{
+ if (isVisible() && canvas()) {
+ hide();
+ Q3CanvasItem::move(nx,ny);
+ if (nf >= 0 && nf < frameCount())
+ frm=nf;
+ show();
+ } else {
+ Q3CanvasItem::move(nx,ny);
+ if (nf >= 0 && nf < frameCount())
+ frm=nf;
+ }
+}
+
+class Q3CanvasPolygonScanner : public Q3PolygonScanner {
+ QPolygonalProcessor& processor;
+public:
+ Q3CanvasPolygonScanner(QPolygonalProcessor& p) :
+ processor(p)
+ {
+ }
+ void processSpans(int n, QPoint* point, int* width)
+ {
+ processor.doSpans(n,point,width);
+ }
+};
+
+void Q3CanvasPolygonalItem::scanPolygon(const Q3PointArray& pa, int winding, QPolygonalProcessor& process) const
+{
+ Q3CanvasPolygonScanner scanner(process);
+ scanner.scan(pa,winding);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/canvas/q3canvas.h b/src/qt3support/canvas/q3canvas.h
new file mode 100644
index 0000000..26b82c5
--- /dev/null
+++ b/src/qt3support/canvas/q3canvas.h
@@ -0,0 +1,787 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3CANVAS_H
+#define Q3CANVAS_H
+
+#include <Qt3Support/q3scrollview.h>
+#include <QtGui/qpixmap.h>
+#include <Qt3Support/q3ptrlist.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qpen.h>
+#include <Qt3Support/q3valuelist.h>
+#include <Qt3Support/q3pointarray.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+class Q3CanvasSprite;
+class Q3CanvasPolygonalItem;
+class Q3CanvasRectangle;
+class Q3CanvasPolygon;
+class Q3CanvasEllipse;
+class Q3CanvasText;
+class Q3CanvasLine;
+class Q3CanvasChunk;
+class Q3Canvas;
+class Q3CanvasItem;
+class Q3CanvasView;
+class Q3CanvasPixmap;
+
+class Q_COMPAT_EXPORT Q3CanvasItemList : public Q3ValueList<Q3CanvasItem*> {
+public:
+ void sort();
+ void drawUnique(QPainter& painter);
+ Q3CanvasItemList operator+(const Q3CanvasItemList &l) const;
+};
+
+
+class Q3CanvasItemExtra;
+
+class Q_COMPAT_EXPORT Q3CanvasItem
+{
+public:
+ Q3CanvasItem(Q3Canvas* canvas);
+ virtual ~Q3CanvasItem();
+
+ double x() const
+ { return myx; }
+ double y() const
+ { return myy; }
+ double z() const
+ { return myz; } // (depth)
+
+ virtual void moveBy(double dx, double dy);
+ void move(double x, double y);
+ void setX(double a) { move(a,y()); }
+ void setY(double a) { move(x(),a); }
+ void setZ(double a) { myz=a; changeChunks(); }
+
+ bool animated() const;
+ virtual void setAnimated(bool y);
+ virtual void setVelocity(double vx, double vy);
+ void setXVelocity(double vx) { setVelocity(vx,yVelocity()); }
+ void setYVelocity(double vy) { setVelocity(xVelocity(),vy); }
+ double xVelocity() const;
+ double yVelocity() const;
+ virtual void advance(int stage);
+
+ virtual bool collidesWith(const Q3CanvasItem*) const=0;
+
+ Q3CanvasItemList collisions(bool exact /* NO DEFAULT */) const;
+
+ virtual void setCanvas(Q3Canvas*);
+
+ virtual void draw(QPainter&)=0;
+
+ void show();
+ void hide();
+
+ virtual void setVisible(bool yes);
+ bool isVisible() const
+ { return (bool)vis; }
+ virtual void setSelected(bool yes);
+ bool isSelected() const
+ { return (bool)sel; }
+ virtual void setEnabled(bool yes);
+ bool isEnabled() const
+ { return (bool)ena; }
+ virtual void setActive(bool yes);
+ bool isActive() const
+ { return (bool)act; }
+ bool visible() const
+ { return (bool)vis; }
+ bool selected() const
+ { return (bool)sel; }
+ bool enabled() const
+ { return (bool)ena; }
+ bool active() const
+ { return (bool)act; }
+
+ enum RttiValues {
+ Rtti_Item = 0,
+ Rtti_Sprite = 1,
+ Rtti_PolygonalItem = 2,
+ Rtti_Text = 3,
+ Rtti_Polygon = 4,
+ Rtti_Rectangle = 5,
+ Rtti_Ellipse = 6,
+ Rtti_Line = 7,
+ Rtti_Spline = 8
+ };
+
+ virtual int rtti() const;
+ static int RTTI;
+
+ virtual QRect boundingRect() const=0;
+ virtual QRect boundingRectAdvanced() const;
+
+ Q3Canvas* canvas() const
+ { return cnv; }
+
+protected:
+ void update() { changeChunks(); }
+
+private:
+ // For friendly subclasses...
+
+ friend class Q3CanvasPolygonalItem;
+ friend class Q3CanvasSprite;
+ friend class Q3CanvasRectangle;
+ friend class Q3CanvasPolygon;
+ friend class Q3CanvasEllipse;
+ friend class Q3CanvasText;
+ friend class Q3CanvasLine;
+
+ virtual Q3PointArray chunks() const;
+ virtual void addToChunks();
+ virtual void removeFromChunks();
+ virtual void changeChunks();
+ virtual bool collidesWith(const Q3CanvasSprite*,
+ const Q3CanvasPolygonalItem*,
+ const Q3CanvasRectangle*,
+ const Q3CanvasEllipse*,
+ const Q3CanvasText*) const = 0;
+ // End of friend stuff
+
+ Q3Canvas* cnv;
+ static Q3Canvas* current_canvas;
+ double myx,myy,myz;
+ Q3CanvasItemExtra *ext;
+ Q3CanvasItemExtra& extra();
+ uint ani:1;
+ uint vis:1;
+ uint val:1;
+ uint sel:1;
+ uint ena:1;
+ uint act:1;
+};
+
+
+class Q3CanvasData;
+
+class Q_COMPAT_EXPORT Q3Canvas : public QObject
+{
+ Q_OBJECT
+public:
+ Q3Canvas(QObject* parent = 0, const char* name = 0);
+ Q3Canvas(int w, int h);
+ Q3Canvas(QPixmap p, int h, int v, int tilewidth, int tileheight);
+
+ virtual ~Q3Canvas();
+
+ virtual void setTiles(QPixmap tiles, int h, int v,
+ int tilewidth, int tileheight);
+ virtual void setBackgroundPixmap(const QPixmap& p);
+ QPixmap backgroundPixmap() const;
+
+ virtual void setBackgroundColor(const QColor& c);
+ QColor backgroundColor() const;
+
+ virtual void setTile(int x, int y, int tilenum);
+ int tile(int x, int y) const
+ { return grid[x+y*htiles]; }
+
+ int tilesHorizontally() const
+ { return htiles; }
+ int tilesVertically() const
+ { return vtiles; }
+
+ int tileWidth() const
+ { return tilew; }
+ int tileHeight() const
+ { return tileh; }
+
+ virtual void resize(int width, int height);
+ int width() const
+ { return awidth; }
+ int height() const
+ { return aheight; }
+ QSize size() const
+ { return QSize(awidth,aheight); }
+ QRect rect() const
+ { return QRect(0, 0, awidth, aheight); }
+ bool onCanvas(int x, int y) const
+ { return x>=0 && y>=0 && x<awidth && y<aheight; }
+ bool onCanvas(const QPoint& p) const
+ { return onCanvas(p.x(),p.y()); }
+ bool validChunk(int x, int y) const
+ { return x>=0 && y>=0 && x<chwidth && y<chheight; }
+ bool validChunk(const QPoint& p) const
+ { return validChunk(p.x(),p.y()); }
+
+ int chunkSize() const
+ { return chunksize; }
+ virtual void retune(int chunksize, int maxclusters=100);
+
+ bool sameChunk(int x1, int y1, int x2, int y2) const
+ { return x1/chunksize==x2/chunksize && y1/chunksize==y2/chunksize; }
+ virtual void setChangedChunk(int i, int j);
+ virtual void setChangedChunkContaining(int x, int y);
+ virtual void setAllChanged();
+ virtual void setChanged(const QRect& area);
+ virtual void setUnchanged(const QRect& area);
+
+ // These call setChangedChunk.
+ void addItemToChunk(Q3CanvasItem*, int i, int j);
+ void removeItemFromChunk(Q3CanvasItem*, int i, int j);
+ void addItemToChunkContaining(Q3CanvasItem*, int x, int y);
+ void removeItemFromChunkContaining(Q3CanvasItem*, int x, int y);
+
+ Q3CanvasItemList allItems();
+ Q3CanvasItemList collisions(const QPoint&) const;
+ Q3CanvasItemList collisions(const QRect&) const;
+ Q3CanvasItemList collisions(const Q3PointArray& pa, const Q3CanvasItem* item,
+ bool exact) const;
+
+ void drawArea(const QRect&, QPainter* p, bool double_buffer=false);
+
+ // These are for Q3CanvasView to call
+ virtual void addView(Q3CanvasView*);
+ virtual void removeView(Q3CanvasView*);
+ void drawCanvasArea(const QRect&, QPainter* p=0, bool double_buffer=true);
+ void drawViewArea(Q3CanvasView* view, QPainter* p, const QRect& r, bool dbuf);
+
+ // These are for Q3CanvasItem to call
+ virtual void addItem(Q3CanvasItem*);
+ virtual void addAnimation(Q3CanvasItem*);
+ virtual void removeItem(Q3CanvasItem*);
+ virtual void removeAnimation(Q3CanvasItem*);
+
+ virtual void setAdvancePeriod(int ms);
+ virtual void setUpdatePeriod(int ms);
+
+ virtual void setDoubleBuffering(bool y);
+
+Q_SIGNALS:
+ void resized();
+
+public Q_SLOTS:
+ virtual void advance();
+ virtual void update();
+
+protected:
+ virtual void drawBackground(QPainter&, const QRect& area);
+ virtual void drawForeground(QPainter&, const QRect& area);
+
+private:
+ void init(int w, int h, int chunksze=16, int maxclust=100);
+
+ Q3CanvasChunk& chunk(int i, int j) const;
+ Q3CanvasChunk& chunkContaining(int x, int y) const;
+
+ QRect changeBounds(const QRect& inarea);
+
+ void ensureOffScrSize(int osw, int osh);
+ QPixmap offscr;
+ int awidth,aheight;
+ int chunksize;
+ int maxclusters;
+ int chwidth,chheight;
+ Q3CanvasChunk* chunks;
+
+ Q3CanvasData* d;
+
+ void initTiles(QPixmap p, int h, int v, int tilewidth, int tileheight);
+ ushort *grid;
+ ushort htiles;
+ ushort vtiles;
+ ushort tilew;
+ ushort tileh;
+ bool oneone;
+ QPixmap pm;
+ QTimer* update_timer;
+ QColor bgcolor;
+ bool debug_redraw_areas;
+ bool dblbuf;
+
+ friend void qt_unview(Q3Canvas* c);
+
+ Q_DISABLE_COPY(Q3Canvas)
+};
+
+class Q3CanvasViewData;
+
+class Q_COMPAT_EXPORT Q3CanvasView : public Q3ScrollView
+{
+ Q_OBJECT
+public:
+
+ Q3CanvasView(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0);
+ Q3CanvasView(Q3Canvas* viewing, QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0);
+ ~Q3CanvasView();
+
+ Q3Canvas* canvas() const
+ { return viewing; }
+ void setCanvas(Q3Canvas* v);
+
+ const QMatrix &worldMatrix() const;
+ const QMatrix &inverseWorldMatrix() const;
+ bool setWorldMatrix(const QMatrix &);
+
+protected:
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ QSize sizeHint() const;
+
+private:
+ friend class Q3Canvas;
+ void drawContents(QPainter*);
+ Q3Canvas* viewing;
+ Q3CanvasViewData* d;
+ friend void qt_unview(Q3Canvas* c);
+
+private Q_SLOTS:
+ void updateContentsSize();
+
+private:
+ Q_DISABLE_COPY(Q3CanvasView)
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasPixmap : public QPixmap
+{
+public:
+#ifndef QT_NO_IMAGEIO
+ Q3CanvasPixmap(const QString& datafilename);
+#endif
+ Q3CanvasPixmap(const QImage& image);
+ Q3CanvasPixmap(const QPixmap&, const QPoint& hotspot);
+ ~Q3CanvasPixmap();
+
+ int offsetX() const
+ { return hotx; }
+ int offsetY() const
+ { return hoty; }
+ void setOffset(int x, int y) { hotx = x; hoty = y; }
+
+private:
+ Q_DISABLE_COPY(Q3CanvasPixmap)
+
+ void init(const QImage&);
+ void init(const QPixmap& pixmap, int hx, int hy);
+
+ friend class Q3CanvasSprite;
+ friend class Q3CanvasPixmapArray;
+ friend bool qt_testCollision(const Q3CanvasSprite* s1, const Q3CanvasSprite* s2);
+
+ int hotx,hoty;
+
+ QImage* collision_mask;
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasPixmapArray
+{
+public:
+ Q3CanvasPixmapArray();
+#ifndef QT_NO_IMAGEIO
+ Q3CanvasPixmapArray(const QString& datafilenamepattern, int framecount=0);
+#endif
+ // this form is deprecated
+ Q3CanvasPixmapArray(Q3PtrList<QPixmap>, Q3PtrList<QPoint> hotspots);
+
+ Q3CanvasPixmapArray(Q3ValueList<QPixmap>, Q3PointArray hotspots = Q3PointArray());
+ ~Q3CanvasPixmapArray();
+
+#ifndef QT_NO_IMAGEIO
+ bool readPixmaps(const QString& datafilenamepattern, int framecount=0);
+ bool readCollisionMasks(const QString& filenamepattern);
+#endif
+
+ // deprecated
+ bool operator!(); // Failure check.
+ bool isValid() const;
+
+ Q3CanvasPixmap* image(int i) const
+ { return img ? img[i] : 0; }
+ void setImage(int i, Q3CanvasPixmap* p);
+ uint count() const
+ { return (uint)framecount; }
+
+private:
+ Q_DISABLE_COPY(Q3CanvasPixmapArray)
+
+#ifndef QT_NO_IMAGEIO
+ bool readPixmaps(const QString& datafilenamepattern, int framecount, bool maskonly);
+#endif
+
+ void reset();
+ int framecount;
+ Q3CanvasPixmap** img;
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasSprite : public Q3CanvasItem
+{
+public:
+ Q3CanvasSprite(Q3CanvasPixmapArray* array, Q3Canvas* canvas);
+
+ void setSequence(Q3CanvasPixmapArray* seq);
+
+ virtual ~Q3CanvasSprite();
+
+ void move(double x, double y);
+ virtual void move(double x, double y, int frame);
+ void setFrame(int);
+ enum FrameAnimationType { Cycle, Oscillate };
+ virtual void setFrameAnimation(FrameAnimationType=Cycle, int step=1, int state=0);
+ int frame() const
+ { return frm; }
+ int frameCount() const
+ { return images->count(); }
+
+ int rtti() const;
+ static int RTTI;
+
+ bool collidesWith(const Q3CanvasItem*) const;
+
+ QRect boundingRect() const;
+
+ // is there a reason for these to be protected? Lars
+//protected:
+
+ int width() const;
+ int height() const;
+
+ int leftEdge() const;
+ int topEdge() const;
+ int rightEdge() const;
+ int bottomEdge() const;
+
+ int leftEdge(int nx) const;
+ int topEdge(int ny) const;
+ int rightEdge(int nx) const;
+ int bottomEdge(int ny) const;
+ Q3CanvasPixmap* image() const
+ { return images->image(frm); }
+ virtual Q3CanvasPixmap* imageAdvanced() const;
+ Q3CanvasPixmap* image(int f) const
+ { return images->image(f); }
+ virtual void advance(int stage);
+
+public:
+ void draw(QPainter& painter);
+
+private:
+ Q_DISABLE_COPY(Q3CanvasSprite)
+
+ void addToChunks();
+ void removeFromChunks();
+ void changeChunks();
+
+ int frm;
+ ushort anim_val;
+ uint anim_state:2;
+ uint anim_type:14;
+ bool collidesWith(const Q3CanvasSprite*,
+ const Q3CanvasPolygonalItem*,
+ const Q3CanvasRectangle*,
+ const Q3CanvasEllipse*,
+ const Q3CanvasText*) const;
+
+ friend bool qt_testCollision(const Q3CanvasSprite* s1,
+ const Q3CanvasSprite* s2);
+
+ Q3CanvasPixmapArray* images;
+};
+
+class QPolygonalProcessor;
+
+class Q_COMPAT_EXPORT Q3CanvasPolygonalItem : public Q3CanvasItem
+{
+public:
+ Q3CanvasPolygonalItem(Q3Canvas* canvas);
+ virtual ~Q3CanvasPolygonalItem();
+
+ bool collidesWith(const Q3CanvasItem*) const;
+
+ virtual void setPen(QPen p);
+ virtual void setBrush(QBrush b);
+
+ QPen pen() const
+ { return pn; }
+ QBrush brush() const
+ { return br; }
+
+ virtual Q3PointArray areaPoints() const=0;
+ virtual Q3PointArray areaPointsAdvanced() const;
+ QRect boundingRect() const;
+
+ int rtti() const;
+ static int RTTI;
+
+protected:
+ void draw(QPainter &);
+ virtual void drawShape(QPainter &) = 0;
+
+ bool winding() const;
+ void setWinding(bool);
+
+ void invalidate();
+ bool isValid() const
+ { return (bool)val; }
+
+private:
+ void scanPolygon(const Q3PointArray& pa, int winding,
+ QPolygonalProcessor& process) const;
+ Q3PointArray chunks() const;
+
+ bool collidesWith(const Q3CanvasSprite*,
+ const Q3CanvasPolygonalItem*,
+ const Q3CanvasRectangle*,
+ const Q3CanvasEllipse*,
+ const Q3CanvasText*) const;
+
+ QBrush br;
+ QPen pn;
+ uint wind:1;
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasRectangle : public Q3CanvasPolygonalItem
+{
+public:
+ Q3CanvasRectangle(Q3Canvas* canvas);
+ Q3CanvasRectangle(const QRect&, Q3Canvas* canvas);
+ Q3CanvasRectangle(int x, int y, int width, int height, Q3Canvas* canvas);
+
+ ~Q3CanvasRectangle();
+
+ int width() const;
+ int height() const;
+ void setSize(int w, int h);
+ QSize size() const
+ { return QSize(w,h); }
+ Q3PointArray areaPoints() const;
+ QRect rect() const
+ { return QRect(int(x()),int(y()),w,h); }
+
+ bool collidesWith(const Q3CanvasItem*) const;
+
+ int rtti() const;
+ static int RTTI;
+
+protected:
+ void drawShape(QPainter &);
+ Q3PointArray chunks() const;
+
+private:
+ bool collidesWith( const Q3CanvasSprite*,
+ const Q3CanvasPolygonalItem*,
+ const Q3CanvasRectangle*,
+ const Q3CanvasEllipse*,
+ const Q3CanvasText*) const;
+
+ int w, h;
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasPolygon : public Q3CanvasPolygonalItem
+{
+public:
+ Q3CanvasPolygon(Q3Canvas* canvas);
+ ~Q3CanvasPolygon();
+ void setPoints(Q3PointArray);
+ Q3PointArray points() const;
+ void moveBy(double dx, double dy);
+
+ Q3PointArray areaPoints() const;
+
+ int rtti() const;
+ static int RTTI;
+
+protected:
+ void drawShape(QPainter &);
+ Q3PointArray poly;
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasSpline : public Q3CanvasPolygon
+{
+public:
+ Q3CanvasSpline(Q3Canvas* canvas);
+ ~Q3CanvasSpline();
+
+ void setControlPoints(Q3PointArray, bool closed=true);
+ Q3PointArray controlPoints() const;
+ bool closed() const;
+
+ int rtti() const;
+ static int RTTI;
+
+private:
+ void recalcPoly();
+ Q3PointArray bez;
+ bool cl;
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasLine : public Q3CanvasPolygonalItem
+{
+public:
+ Q3CanvasLine(Q3Canvas* canvas);
+ ~Q3CanvasLine();
+ void setPoints(int x1, int y1, int x2, int y2);
+
+ QPoint startPoint() const
+ { return QPoint(x1,y1); }
+ QPoint endPoint() const
+ { return QPoint(x2,y2); }
+
+ int rtti() const;
+ static int RTTI;
+
+ void setPen(QPen p);
+ void moveBy(double dx, double dy);
+
+protected:
+ void drawShape(QPainter &);
+ Q3PointArray areaPoints() const;
+
+private:
+ int x1,y1,x2,y2;
+};
+
+
+class Q_COMPAT_EXPORT Q3CanvasEllipse : public Q3CanvasPolygonalItem
+{
+
+public:
+ Q3CanvasEllipse(Q3Canvas* canvas);
+ Q3CanvasEllipse(int width, int height, Q3Canvas* canvas);
+ Q3CanvasEllipse(int width, int height, int startangle, int angle,
+ Q3Canvas* canvas);
+
+ ~Q3CanvasEllipse();
+
+ int width() const;
+ int height() const;
+ void setSize(int w, int h);
+ void setAngles(int start, int length);
+ int angleStart() const
+ { return a1; }
+ int angleLength() const
+ { return a2; }
+ Q3PointArray areaPoints() const;
+
+ bool collidesWith(const Q3CanvasItem*) const;
+
+ int rtti() const;
+ static int RTTI;
+
+protected:
+ void drawShape(QPainter &);
+
+private:
+ bool collidesWith(const Q3CanvasSprite*,
+ const Q3CanvasPolygonalItem*,
+ const Q3CanvasRectangle*,
+ const Q3CanvasEllipse*,
+ const Q3CanvasText*) const;
+ int w, h;
+ int a1, a2;
+};
+
+
+class Q3CanvasTextExtra;
+
+class Q_COMPAT_EXPORT Q3CanvasText : public Q3CanvasItem
+{
+public:
+ Q3CanvasText(Q3Canvas* canvas);
+ Q3CanvasText(const QString&, Q3Canvas* canvas);
+ Q3CanvasText(const QString&, QFont, Q3Canvas* canvas);
+
+ virtual ~Q3CanvasText();
+
+ void setText(const QString&);
+ void setFont(const QFont&);
+ void setColor(const QColor&);
+ QString text() const;
+ QFont font() const;
+ QColor color() const;
+
+ void moveBy(double dx, double dy);
+
+ int textFlags() const
+ { return flags; }
+ void setTextFlags(int);
+
+ QRect boundingRect() const;
+
+ bool collidesWith(const Q3CanvasItem*) const;
+
+ int rtti() const;
+ static int RTTI;
+
+protected:
+ virtual void draw(QPainter&);
+
+private:
+ Q_DISABLE_COPY(Q3CanvasText)
+
+ void addToChunks();
+ void removeFromChunks();
+ void changeChunks();
+
+ void setRect();
+ QRect brect;
+ QString txt;
+ int flags;
+ QFont fnt;
+ QColor col;
+ Q3CanvasTextExtra* extra;
+
+ bool collidesWith(const Q3CanvasSprite*,
+ const Q3CanvasPolygonalItem*,
+ const Q3CanvasRectangle*,
+ const Q3CanvasEllipse*,
+ const Q3CanvasText*) const;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3CANVAS_H
diff --git a/src/qt3support/dialogs/dialogs.pri b/src/qt3support/dialogs/dialogs.pri
new file mode 100644
index 0000000..7ea5fb2
--- /dev/null
+++ b/src/qt3support/dialogs/dialogs.pri
@@ -0,0 +1,16 @@
+# Qt compat module
+
+HEADERS += \
+ dialogs/q3filedialog.h \
+ dialogs/q3tabdialog.h \
+ dialogs/q3progressdialog.h \
+ dialogs/q3wizard.h
+
+SOURCES += \
+ dialogs/q3filedialog.cpp \
+ dialogs/q3progressdialog.cpp \
+ dialogs/q3tabdialog.cpp \
+ dialogs/q3wizard.cpp
+
+win32:SOURCES += dialogs/q3filedialog_win.cpp
+!embedded:mac:SOURCES += dialogs/q3filedialog_mac.cpp
diff --git a/src/qt3support/dialogs/q3filedialog.cpp b/src/qt3support/dialogs/q3filedialog.cpp
new file mode 100644
index 0000000..35eef0d
--- /dev/null
+++ b/src/qt3support/dialogs/q3filedialog.cpp
@@ -0,0 +1,6067 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "q3filedialog.h"
+
+#ifndef QT_NO_FILEDIALOG
+
+#include "private/qapplication_p.h"
+#include "q3buttongroup.h"
+#include "q3header.h"
+#include "q3listview.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qcheckbox.h"
+#include "q3cleanuphandler.h"
+#include "qcombobox.h"
+#include "q3combobox.h"
+#include "q3cstring.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "q3dragobject.h"
+#include "qevent.h"
+#include "qfile.h"
+#include "qlabel.h"
+#include "qlayout.h"
+#include "qlibrary.h"
+#include "qlineedit.h"
+#include "q3listbox.h"
+#include "qmap.h"
+#include "qmessagebox.h"
+#include "qmime.h"
+#include "qpainter.h"
+#include "qpointer.h"
+#include "q3popupmenu.h"
+#include "q3progressbar.h"
+#include "q3ptrvector.h"
+#include "qpushbutton.h"
+#include "qregexp.h"
+#include "qsplitter.h"
+#include "q3strlist.h"
+#include "qstyle.h"
+#include "qtimer.h"
+#include "qtoolbutton.h"
+#include "qtooltip.h"
+#include "q3widgetstack.h"
+#include "q3urloperator.h"
+#include "q3vbox.h"
+#include "qurlinfo.h"
+
+#ifdef Q_WS_WIN
+#ifndef QT_NO_THREAD
+# include "qwindowsstyle.h"
+# include "private/qmutexpool_p.h"
+#endif
+#endif // Q_WS_WIN
+
+#ifndef Q_OS_WINCE
+#include <time.h>
+#else
+#include <shellapi.h>
+#endif // Q_OS_WINCE
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+
+#ifdef Q_WS_MAC
+#include "qmacstyle_mac.h"
+#include "private/qt_mac_p.h"
+#undef check
+#endif
+
+#if defined(Q_OS_OPENBSD)
+#include <sys/param.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/* XPM */
+static const char * const start_xpm[]={
+ "16 15 8 1",
+ "a c #cec6bd",
+ "# c #000000",
+ "e c #ffff00",
+ "b c #999999",
+ "f c #cccccc",
+ "d c #dcdcdc",
+ "c c #ffffff",
+ ". c None",
+ ".....######aaaaa",
+ "...bb#cccc##aaaa",
+ "..bcc#cccc#d#aaa",
+ ".bcef#cccc#dd#aa",
+ ".bcfe#cccc#####a",
+ ".bcef#ccccccccc#",
+ "bbbbbbbbbbbbccc#",
+ "bccccccccccbbcc#",
+ "bcefefefefee#bc#",
+ ".bcefefefefef#c#",
+ ".bcfefefefefe#c#",
+ "..bcfefefefeeb##",
+ "..bbbbbbbbbbbbb#",
+ "...#############",
+ "................"};
+
+/* XPM */
+static const char * const end_xpm[]={
+ "16 15 9 1",
+ "d c #a0a0a0",
+ "c c #c3c3c3",
+ "# c #cec6bd",
+ ". c #000000",
+ "f c #ffff00",
+ "e c #999999",
+ "g c #cccccc",
+ "b c #ffffff",
+ "a c None",
+ "......####aaaaaa",
+ ".bbbb..###aaaaaa",
+ ".bbbb.c.##aaaaaa",
+ ".bbbb....ddeeeea",
+ ".bbbbbbb.bbbbbe.",
+ ".bbbbbbb.bcfgfe.",
+ "eeeeeeeeeeeeefe.",
+ "ebbbbbbbbbbeege.",
+ "ebfgfgfgfgff.ee.",
+ "aebfgfgfgfgfg.e.",
+ "aebgfgfgfgfgf.e.",
+ "aaebgfgfgfgffe..",
+ "aaeeeeeeeeeeeee.",
+ "aaa.............",
+ "aaaaaaaaaaaaaaaa"};
+
+/* XPM */
+static const char* const open_xpm[]={
+ "16 16 6 1",
+ ". c None",
+ "b c #ffff00",
+ "d c #000000",
+ "* c #999999",
+ "c c #cccccc",
+ "a c #ffffff",
+ "................",
+ "................",
+ "...*****........",
+ "..*aaaaa*.......",
+ ".*abcbcba******.",
+ ".*acbcbcaaaaaa*d",
+ ".*abcbcbcbcbcb*d",
+ "*************b*d",
+ "*aaaaaaaaaa**c*d",
+ "*abcbcbcbcbbd**d",
+ ".*abcbcbcbcbcd*d",
+ ".*acbcbcbcbcbd*d",
+ "..*acbcbcbcbb*dd",
+ "..*************d",
+ "...ddddddddddddd",
+ "................"};
+
+/* XPM */
+static const char * const link_dir_xpm[]={
+ "16 16 10 1",
+ "h c #808080",
+ "g c #a0a0a0",
+ "d c #000000",
+ "b c #ffff00",
+ "f c #303030",
+ "# c #999999",
+ "a c #cccccc",
+ "e c #585858",
+ "c c #ffffff",
+ ". c None",
+ "................",
+ "................",
+ "..#####.........",
+ ".#ababa#........",
+ "#abababa######..",
+ "#cccccccccccc#d.",
+ "#cbababababab#d.",
+ "#cabababababa#d.",
+ "#cbababdddddddd.",
+ "#cababadccccccd.",
+ "#cbababdcececcd.",
+ "#cababadcefdfcd.",
+ "#cbababdccgdhcd.",
+ "#######dccchccd.",
+ ".dddddddddddddd.",
+ "................"};
+
+/* XPM */
+static const char * const link_file_xpm[]={
+ "16 16 10 1",
+ "h c #808080",
+ "g c #a0a0a0",
+ "d c #c3c3c3",
+ ". c #7f7f7f",
+ "c c #000000",
+ "b c #bfbfbf",
+ "f c #303030",
+ "e c #585858",
+ "a c #ffffff",
+ "# c None",
+ "################",
+ "..........######",
+ ".aaaaaaaab.#####",
+ ".aaaaaaaaba.####",
+ ".aaaaaaaacccc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaadc###",
+ ".aaaaaaaaaadc###",
+ ".aaaacccccccc###",
+ ".aaaacaaaaaac###",
+ ".aaaacaeaeaac###",
+ ".aaaacaefcfac###",
+ ".aaaacaagchac###",
+ ".ddddcaaahaac###",
+ "ccccccccccccc###"};
+
+/* XPM */
+static const char* const file_xpm[]={
+ "16 16 5 1",
+ ". c #7f7f7f",
+ "# c None",
+ "c c #000000",
+ "b c #bfbfbf",
+ "a c #ffffff",
+ "################",
+ "..........######",
+ ".aaaaaaaab.#####",
+ ".aaaaaaaaba.####",
+ ".aaaaaaaacccc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".aaaaaaaaaabc###",
+ ".bbbbbbbbbbbc###",
+ "ccccccccccccc###"};
+
+/* XPM */
+static const char * const closed_xpm[]={
+ "16 16 6 1",
+ ". c None",
+ "b c #ffff00",
+ "d c #000000",
+ "* c #999999",
+ "a c #cccccc",
+ "c c #ffffff",
+ "................",
+ "................",
+ "..*****.........",
+ ".*ababa*........",
+ "*abababa******..",
+ "*cccccccccccc*d.",
+ "*cbababababab*d.",
+ "*cabababababa*d.",
+ "*cbababababab*d.",
+ "*cabababababa*d.",
+ "*cbababababab*d.",
+ "*cabababababa*d.",
+ "*cbababababab*d.",
+ "**************d.",
+ ".dddddddddddddd.",
+ "................"};
+
+
+/* XPM */
+static const char* const cdtoparent_xpm[]={
+ "15 13 3 1",
+ ". c None",
+ "* c #000000",
+ "a c #ffff99",
+ "..*****........",
+ ".*aaaaa*.......",
+ "***************",
+ "*aaaaaaaaaaaaa*",
+ "*aaaa*aaaaaaaa*",
+ "*aaa***aaaaaaa*",
+ "*aa*****aaaaaa*",
+ "*aaaa*aaaaaaaa*",
+ "*aaaa*aaaaaaaa*",
+ "*aaaa******aaa*",
+ "*aaaaaaaaaaaaa*",
+ "*aaaaaaaaaaaaa*",
+ "***************"};
+
+
+/* XPM */
+static const char* const newfolder_xpm[] = {
+ "15 14 4 1",
+ " c None",
+ ". c #000000",
+ "+ c #FFFF00",
+ "@ c #FFFFFF",
+ " . ",
+ " ",
+ " . ",
+ " . . ",
+ " .... . . . ",
+ " .+@+@. . . ",
+ ".......... . .",
+ ".@+@+@+@+@.. ",
+ ".+@+@+@+@+. . ",
+ ".@+@+@+@+@. . ",
+ ".+@+@+@+@+. ",
+ ".@+@+@+@+@. ",
+ ".+@+@+@+@+. ",
+ "........... "};
+
+/* XPM */
+static const char* const detailedview_xpm[]={
+ "14 11 3 1",
+ ". c None",
+ "* c #000000",
+ "a c #000099",
+ ".****.***.***.",
+ "..............",
+ "aaaaaaaaaaaaaa",
+ "..............",
+ ".****.***.***.",
+ "..............",
+ ".****.***.***.",
+ "..............",
+ ".****.***.***.",
+ "..............",
+ ".****.***.***."};
+
+/* XPM */
+static const char* const previewinfoview_xpm[]={
+ "13 13 4 1",
+ ". c #00007f",
+ "a c black",
+ "# c #cec6bd",
+ "b c #000000",
+ "..#####aaaaaa",
+ ".#.#bb#a#####",
+ "...####a#bbb#",
+ "#######a#####",
+ "#######a#bb##",
+ "..#####a#####",
+ ".#.#bb#a#bbb#",
+ "...####a#####",
+ "#######a#bb##",
+ "#######a#####",
+ "..#####a#bbb#",
+ ".#.#bb#a#####",
+ "...####aaaaaa"};
+
+/* XPM */
+static const char* const previewcontentsview_xpm[]={
+ "14 13 5 1",
+ ". c #00007f",
+ "a c black",
+ "c c #7f007f",
+ "# c #cec6bd",
+ "b c #000000",
+ "..#####aaaaaaa",
+ ".#.#bb#a#####a",
+ "...####a#ccc#a",
+ "#######a#ccc#a",
+ "#######a#####a",
+ "..#####a#bbb#a",
+ ".#.#bb#a#####a",
+ "...####a#bbb#a",
+ "#######a#####a",
+ "#######a#bbb#a",
+ "..#####a#####a",
+ ".#.#bb#a#####a",
+ "...####aaaaaaa"};
+
+/* XPM */
+static const char* const mclistview_xpm[]={
+ "15 11 4 1",
+ "* c None",
+ "b c #000000",
+ ". c #000099",
+ "a c #ffffff",
+ "...*****...****",
+ ".a.*bbb*.a.*bbb",
+ "...*****...****",
+ "***************",
+ "...*****...****",
+ ".a.*bbb*.a.*bbb",
+ "...*****...****",
+ "***************",
+ "...*****...****",
+ ".a.*bbb*.a.*bbb",
+ "...*****...****"};
+
+/* XPM */
+static const char * const back_xpm [] = {
+ "13 11 3 1",
+ "a c #00ffff",
+ "# c #000000",
+ ". c None",
+ ".....#.......",
+ "....##.......",
+ "...#a#.......",
+ "..#aa########",
+ ".#aaaaaaaaaa#",
+ "#aaaaaaaaaaa#",
+ ".#aaaaaaaaaa#",
+ "..#aa########",
+ "...#a#.......",
+ "....##.......",
+ ".....#......."};
+
+static QPixmap * openFolderIcon = 0;
+static QPixmap * closedFolderIcon = 0;
+static QPixmap * detailViewIcon = 0;
+static QPixmap * multiColumnListViewIcon = 0;
+static QPixmap * cdToParentIcon = 0;
+static QPixmap * newFolderIcon = 0;
+static QPixmap * fifteenTransparentPixels = 0;
+static QPixmap * symLinkDirIcon = 0;
+static QPixmap * symLinkFileIcon = 0;
+static QPixmap * fileIcon = 0;
+static QPixmap * startCopyIcon = 0;
+static QPixmap * endCopyIcon = 0;
+static QPixmap * previewContentsViewIcon = 0;
+static QPixmap * previewInfoViewIcon = 0;
+static QPixmap *goBackIcon = 0;
+static Q3FileIconProvider * fileIconProvider = 0;
+static int lastWidth = 0;
+static int lastHeight = 0;
+static QString * workingDirectory = 0;
+
+static bool bShowHiddenFiles = false;
+static int sortFilesBy = (int)QDir::Name;
+static bool sortAscending = true;
+static bool detailViewMode = false;
+
+static Q3CleanupHandler<QString> qfd_cleanup_string;
+
+static void qt_cleanup_fd_pixmaps();
+typedef QList<QPixmap *> FDPixmaps;
+Q_GLOBAL_STATIC_WITH_INITIALIZER(FDPixmaps, qfd_pixmaps, qAddPostRoutine(qt_cleanup_fd_pixmaps))
+
+static void qt_cleanup_fd_pixmaps()
+{
+ qDeleteAll(*qfd_pixmaps());
+}
+
+static QString toRootIfNotExists( const QString &path )
+{
+ if ( !path.isEmpty() )
+ return path;
+
+ QFileInfoList drives = QDir::drives();
+ Q_ASSERT( !drives.isEmpty() );
+ return drives.first().filePath();
+}
+
+static bool isDirectoryMode(int m)
+{
+ return m == Q3FileDialog::Directory || m == Q3FileDialog::DirectoryOnly;
+}
+
+static void updateLastSize(Q3FileDialog *that)
+{
+ int extWidth = 0;
+ int extHeight = 0;
+ if (that->extension() && that->extension()->isVisible()) {
+ if (that->orientation() == Qt::Vertical)
+ extHeight = that->extension()->height();
+ else
+ extWidth = that->extension()->width();
+ }
+ lastWidth = that->width() - extWidth;
+ lastHeight = that->height() - extHeight;
+}
+
+#if defined(Q_WS_WIN)
+class QWindowsIconProvider : public Q3FileIconProvider
+{
+public:
+ QWindowsIconProvider(QObject *parent=0, const char *name=0);
+ ~QWindowsIconProvider();
+
+ const QPixmap * pixmap(const QFileInfo &fi);
+
+private:
+ QPixmap defaultFolder;
+ QPixmap defaultFile;
+ QPixmap defaultExe;
+ QPixmap pix;
+ int pixw, pixh;
+ QMap< QString, QPixmap > cache;
+
+};
+#endif
+
+static void makeVariables() {
+ if (!openFolderIcon) {
+ workingDirectory = new QString(toRootIfNotExists( QDir::currentDirPath() ));
+ qfd_cleanup_string.add(&workingDirectory);
+
+ openFolderIcon = new QPixmap((const char **)open_xpm);
+ qfd_pixmaps()->append(openFolderIcon);
+ symLinkDirIcon = new QPixmap((const char **)link_dir_xpm);
+ qfd_pixmaps()->append(symLinkDirIcon);
+ symLinkFileIcon = new QPixmap((const char **)link_file_xpm);
+ qfd_pixmaps()->append(symLinkFileIcon);
+ fileIcon = new QPixmap((const char **)file_xpm);
+ qfd_pixmaps()->append(fileIcon);
+ closedFolderIcon = new QPixmap((const char **)closed_xpm);
+ qfd_pixmaps()->append(closedFolderIcon);
+ detailViewIcon = new QPixmap((const char **)detailedview_xpm);
+ qfd_pixmaps()->append(detailViewIcon);
+ multiColumnListViewIcon = new QPixmap((const char **)mclistview_xpm);
+ qfd_pixmaps()->append(multiColumnListViewIcon);
+ cdToParentIcon = new QPixmap((const char **)cdtoparent_xpm);
+ qfd_pixmaps()->append(cdToParentIcon);
+ newFolderIcon = new QPixmap((const char **)newfolder_xpm);
+ qfd_pixmaps()->append(newFolderIcon);
+ previewInfoViewIcon
+ = new QPixmap((const char **)previewinfoview_xpm);
+ qfd_pixmaps()->append(previewInfoViewIcon);
+ previewContentsViewIcon
+ = new QPixmap((const char **)previewcontentsview_xpm);
+ qfd_pixmaps()->append(previewContentsViewIcon);
+ startCopyIcon = new QPixmap((const char **)start_xpm);
+ qfd_pixmaps()->append(startCopyIcon);
+ endCopyIcon = new QPixmap((const char **)end_xpm);
+ qfd_pixmaps()->append(endCopyIcon);
+ goBackIcon = new QPixmap((const char **)back_xpm);
+ qfd_pixmaps()->append(goBackIcon);
+ fifteenTransparentPixels = new QPixmap(closedFolderIcon->width(), 1);
+ qfd_pixmaps()->append(fifteenTransparentPixels);
+ QBitmap m(fifteenTransparentPixels->width(), 1);
+ m.fill(Qt::color0);
+ fifteenTransparentPixels->setMask(m);
+ bShowHiddenFiles = false;
+ sortFilesBy = (int)QDir::Name;
+ detailViewMode = false;
+#if defined(Q_WS_WIN)
+ if (!fileIconProvider)
+ fileIconProvider = new QWindowsIconProvider(qApp);
+#endif
+ }
+}
+
+/******************************************************************
+ *
+ * Definitions of view classes
+ *
+ ******************************************************************/
+
+class QRenameEdit : public QLineEdit
+{
+ Q_OBJECT
+
+public:
+ QRenameEdit(QWidget *parent);
+
+protected:
+ void keyPressEvent(QKeyEvent *e);
+ void focusOutEvent(QFocusEvent *e);
+ void emitDoRename();
+
+signals:
+ void cancelRename();
+ void doRename();
+
+private slots:
+ void slotReturnPressed();
+
+private:
+ bool doRenameAlreadyEmitted;
+};
+
+QRenameEdit::QRenameEdit(QWidget *parent)
+ : QLineEdit(parent, "qt_rename_edit"), doRenameAlreadyEmitted(false)
+{
+ connect(this, SIGNAL(returnPressed()), SLOT(slotReturnPressed()));
+}
+
+class QFileListBox : public Q3ListBox
+{
+ friend class Q3FileDialog;
+
+ Q_OBJECT
+
+private:
+ QFileListBox(QWidget *parent, Q3FileDialog *d);
+
+ void clear();
+ void show();
+ void startRename(bool check = true);
+ void viewportMousePressEvent(QMouseEvent *e);
+ void viewportMouseReleaseEvent(QMouseEvent *e);
+ void viewportMouseDoubleClickEvent(QMouseEvent *e);
+ void viewportMouseMoveEvent(QMouseEvent *e);
+#ifndef QT_NO_DRAGANDDROP
+ void viewportDragEnterEvent(QDragEnterEvent *e);
+ void viewportDragMoveEvent(QDragMoveEvent *e);
+ void viewportDragLeaveEvent(QDragLeaveEvent *e);
+ void viewportDropEvent(QDropEvent *e);
+ bool acceptDrop(const QPoint &pnt, QWidget *source);
+ void setCurrentDropItem(const QPoint &pnt);
+#endif
+ void keyPressEvent(QKeyEvent *e);
+
+private slots:
+ void rename();
+ void cancelRename();
+ void doubleClickTimeout();
+ void changeDirDuringDrag();
+ void dragObjDestroyed();
+ void contentsMoved(int, int);
+
+private:
+ QRenameEdit *lined;
+ Q3FileDialog *filedialog;
+ bool renaming;
+ QTimer* renameTimer;
+ Q3ListBoxItem *renameItem, *dragItem;
+ QPoint pressPos, oldDragPos;
+ bool mousePressed;
+ int urls;
+ QString startDragDir;
+ Q3ListBoxItem *currDropItem;
+ QTimer *changeDirTimer;
+ bool firstMousePressEvent;
+ Q3UrlOperator startDragUrl;
+
+};
+
+
+class Q3FileDialogQFileListView : public Q3ListView
+{
+ Q_OBJECT
+
+public:
+ Q3FileDialogQFileListView(QWidget *parent, Q3FileDialog *d);
+
+ void clear();
+ void startRename(bool check = true);
+ void setSorting(int column, bool increasing = true);
+
+ QRenameEdit *lined;
+ bool renaming;
+ Q3ListViewItem *renameItem;
+
+private:
+ void viewportMousePressEvent(QMouseEvent *e);
+ void viewportMouseDoubleClickEvent(QMouseEvent *e);
+ void keyPressEvent(QKeyEvent *e);
+ void viewportMouseReleaseEvent(QMouseEvent *e);
+ void viewportMouseMoveEvent(QMouseEvent *e);
+#ifndef QT_NO_DRAGANDDROP
+ void viewportDragEnterEvent(QDragEnterEvent *e);
+ void viewportDragMoveEvent(QDragMoveEvent *e);
+ void viewportDragLeaveEvent(QDragLeaveEvent *e);
+ void viewportDropEvent(QDropEvent *e);
+ bool acceptDrop(const QPoint &pnt, QWidget *source);
+ void setCurrentDropItem(const QPoint &pnt);
+#endif
+
+private slots:
+ void rename();
+ void cancelRename();
+ void changeSortColumn2(int column);
+ void doubleClickTimeout();
+ void changeDirDuringDrag();
+ void dragObjDestroyed();
+ void contentsMoved(int, int);
+
+private:
+ Q3FileDialog *filedialog;
+ QTimer* renameTimer;
+ QPoint pressPos, oldDragPos;
+ bool mousePressed;
+ int urls;
+ QString startDragDir;
+ Q3ListViewItem *currDropItem, *dragItem;
+ QTimer *changeDirTimer;
+ bool firstMousePressEvent;
+ bool ascending;
+ int sortcolumn;
+ Q3UrlOperator startDragUrl;
+
+};
+
+/****************************************************************************
+ *
+ * Classes for copy progress dialog
+ *
+ ****************************************************************************/
+
+class QFDProgressAnimation : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QFDProgressAnimation(QWidget *parent);
+ void start();
+
+private slots:
+ void next();
+
+protected:
+ void paintEvent(QPaintEvent *e);
+
+private:
+ int step;
+ QTimer *timer;
+
+};
+
+QFDProgressAnimation::QFDProgressAnimation(QWidget *parent)
+ : QWidget(parent, "qt_progressanimation")
+{
+ setFixedSize(300, 50);
+ step = -1;
+ next();
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()),
+ this, SLOT(next()));
+}
+
+void QFDProgressAnimation::start()
+{
+ timer->start(150, false);
+}
+
+void QFDProgressAnimation::next()
+{
+ ++step;
+ if (step > 10)
+ step = 0;
+ repaint();
+}
+
+void QFDProgressAnimation::paintEvent(QPaintEvent *)
+{
+ erase();
+
+ QPainter p;
+ p.begin(this);
+ if (step == 0) {
+ p.drawPixmap(5, (height() - startCopyIcon->height()) / 2,
+ *startCopyIcon);
+ p.drawPixmap(width() - 5 - openFolderIcon->width(),
+ (height() - openFolderIcon->height()) / 2 , *openFolderIcon);
+ } else if (step == 10) {
+ p.drawPixmap(5, (height() - openFolderIcon->height()) / 2,
+ *openFolderIcon);
+ p.drawPixmap(width() - 5 - endCopyIcon->width(),
+ (height() - endCopyIcon->height()) / 2 , *endCopyIcon);
+ } else {
+ p.drawPixmap(5, (height() - openFolderIcon->height()) / 2,
+ *openFolderIcon);
+ p.drawPixmap(width() - 5 - openFolderIcon->width(),
+ (height() - openFolderIcon->height()) / 2 , *openFolderIcon);
+ int x = 10 + openFolderIcon->width();
+ int w = width() - 2 * x;
+ int s = w / 9;
+ p.drawPixmap(x + s * step, (height() - fileIcon->height()) / 2 - fileIcon->height(),
+ *fileIcon);
+ }
+}
+
+
+class QFDProgressDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ QFDProgressDialog(QWidget *parent, const QString &fn, int steps);
+
+ void setReadProgress(int p);
+ void setWriteProgress(int p);
+ void setWriteLabel(const QString &s);
+
+signals:
+ void cancelled();
+
+private:
+ Q3ProgressBar *readBar;
+ Q3ProgressBar *writeBar;
+ QLabel *writeLabel;
+ QFDProgressAnimation *animation;
+
+};
+
+QFDProgressDialog::QFDProgressDialog(QWidget *parent, const QString &fn, int steps)
+ : QDialog(parent, "", true)
+{
+ setWindowTitle(Q3FileDialog::tr("Copy or Move a File"));
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->setSpacing(5);
+ layout->setMargin(5);
+
+ animation = new QFDProgressAnimation(this);
+ layout->addWidget(animation);
+
+ layout->addWidget(new QLabel(Q3FileDialog::tr("Read: %1").arg(fn),
+ this, "qt_read_lbl"));
+ readBar = new Q3ProgressBar(steps, this, "qt_readbar");
+ readBar->reset();
+ readBar->setProgress(0);
+ layout->addWidget(readBar);
+ writeLabel = new QLabel(Q3FileDialog::tr("Write: %1").arg(QString()),
+ this, "qt_write_lbl");
+ layout->addWidget(writeLabel);
+ writeBar = new Q3ProgressBar(steps, this, "qt_writebar");
+ writeBar->reset();
+ writeBar->setProgress(0);
+ layout->addWidget(writeBar);
+
+ QPushButton *b = new QPushButton(Q3FileDialog::tr("Cancel"), this,
+ "qt_cancel_btn");
+ b->setFixedSize(b->sizeHint());
+ layout->addWidget(b);
+ connect(b, SIGNAL(clicked()),
+ this, SIGNAL(cancelled()));
+
+ animation->start();
+}
+
+void QFDProgressDialog::setReadProgress(int p)
+{
+ readBar->setProgress(p);
+}
+
+void QFDProgressDialog::setWriteProgress(int p)
+{
+ writeBar->setProgress(p);
+}
+
+void QFDProgressDialog::setWriteLabel(const QString &s)
+{
+ writeLabel->setText(Q3FileDialog::tr("Write: %1").arg(s));
+}
+
+/************************************************************************
+ *
+ * Private Q3FileDialog members
+ *
+ ************************************************************************/
+
+class Q3FileDialogPrivate {
+public:
+ ~Q3FileDialogPrivate();
+
+ QStringList history;
+
+ bool geometryDirty;
+ Q3ComboBox * paths;
+ QComboBox * types;
+ QLabel * pathL;
+ QLabel * fileL;
+ QLabel * typeL;
+
+ QVBoxLayout * topLevelLayout;
+ QHBoxLayout *buttonLayout, *leftLayout, *rightLayout;
+ Q3PtrList<QHBoxLayout> extraWidgetsLayouts;
+ Q3PtrList<QLabel> extraLabels;
+ Q3PtrList<QWidget> extraWidgets;
+ Q3PtrList<QWidget> extraButtons;
+ Q3PtrList<QAbstractButton> toolButtons;
+
+ Q3WidgetStack * stack;
+
+ QToolButton * cdToParent, *newFolder, * detailView, * mcView,
+ *previewInfo, *previewContents, *goBack;
+ Q3ButtonGroup * modeButtons;
+
+ QString currentFileName;
+ Q3ListViewItem *last;
+
+ Q3ListBoxItem *lastEFSelected;
+
+ struct File: public Q3ListViewItem {
+ File(Q3FileDialogPrivate * dlgp,
+ const QUrlInfo * fi, Q3ListViewItem * parent)
+ : Q3ListViewItem(parent, dlgp->last), info(*fi), d(dlgp), i(0), hasMimePixmap(false)
+ { setup(); dlgp->last = this; }
+ File(Q3FileDialogPrivate * dlgp,
+ const QUrlInfo * fi, Q3ListView * parent)
+ : Q3ListViewItem(parent, dlgp->last), info(*fi), d(dlgp), i(0), hasMimePixmap(false)
+ { setup(); dlgp->last = this; }
+ File(Q3FileDialogPrivate * dlgp,
+ const QUrlInfo * fi, Q3ListView * parent, Q3ListViewItem * after)
+ : Q3ListViewItem(parent, after), info(*fi), d(dlgp), i(0), hasMimePixmap(false)
+ { setup(); if (!nextSibling()) dlgp->last = this; }
+ ~File();
+
+ QString text(int column) const;
+ const QPixmap * pixmap(int) const;
+
+ QUrlInfo info;
+ Q3FileDialogPrivate * d;
+ Q3ListBoxItem *i;
+ bool hasMimePixmap;
+ };
+
+ class MCItem: public Q3ListBoxItem {
+ public:
+ MCItem(Q3ListBox *, Q3ListViewItem * item);
+ MCItem(Q3ListBox *, Q3ListViewItem * item, Q3ListBoxItem *after);
+ QString text() const;
+ const QPixmap *pixmap() const;
+ int height(const Q3ListBox *) const;
+ int width(const Q3ListBox *) const;
+ void paint(QPainter *);
+ Q3ListViewItem * i;
+ };
+
+ class UrlInfoList : public Q3PtrList<QUrlInfo> {
+ public:
+ UrlInfoList() { setAutoDelete(true); }
+ int compareItems(Q3PtrCollection::Item n1, Q3PtrCollection::Item n2) {
+ if (!n1 || !n2)
+ return 0;
+
+ QUrlInfo *i1 = (QUrlInfo *)n1;
+ QUrlInfo *i2 = (QUrlInfo *)n2;
+
+ if (i1->isDir() && !i2->isDir())
+ return -1;
+ if (!i1->isDir() && i2->isDir())
+ return 1;
+
+ if (i1->name() == QLatin1String(".."))
+ return -1;
+ if (i2->name() == QLatin1String(".."))
+ return 1;
+
+ if (sortFilesBy == QDir::Name) {
+#if defined(Q_OS_WIN32)
+ QString name1 = i1->name().lower();
+ QString name2 = i2->name().lower();
+ return name1.localeAwareCompare( name2 );
+#else
+ QString name1 = i1->name();
+ QString name2 = i2->name();
+ return name1.localeAwareCompare( name2 );
+#endif
+ }
+ if (QUrlInfo::equal(*i1, *i2, sortFilesBy))
+ return 0;
+ else if (QUrlInfo::greaterThan(*i1, *i2, sortFilesBy))
+ return 1;
+ else if (QUrlInfo::lessThan(*i1, *i2, sortFilesBy))
+ return -1;
+ // can't happen...
+ return 0;
+ }
+ QUrlInfo *operator[](int i) {
+ return at(i);
+ }
+ };
+
+ UrlInfoList sortedList;
+ Q3PtrList<File> pendingItems;
+
+ QFileListBox * moreFiles;
+
+ Q3FileDialog::Mode mode;
+
+ QString rw;
+ QString ro;
+ QString wo;
+ QString inaccessible;
+
+ QString symLinkToFile;
+ QString file;
+ QString symLinkToDir;
+ QString dir;
+ QString symLinkToSpecial;
+ QString special;
+ Q3WidgetStack *preview;
+ bool infoPreview, contentsPreview;
+ QSplitter *splitter;
+ Q3UrlOperator url, oldUrl;
+ QWidget *infoPreviewWidget, *contentsPreviewWidget;
+ Q3FilePreview *infoPreviewer, *contentsPreviewer;
+ bool hadDotDot;
+
+ bool ignoreNextKeyPress;
+ // ignores the next refresh operation in case the user forced a selection
+ bool ignoreNextRefresh;
+ QFDProgressDialog *progressDia;
+ bool checkForFilter;
+ bool ignoreStop;
+
+ QTimer *mimeTypeTimer;
+ const Q3NetworkOperation *currListChildren;
+
+ // this is similar to QUrl::encode but does encode "*" and
+ // doesn't encode whitespaces
+ static QString encodeFileName(const QString& fName) {
+
+ QString newStr;
+ Q3CString cName = fName.utf8();
+ const Q3CString sChars(
+#ifdef Q_WS_WIN
+ "#%"
+#else
+ "<>#@\"&%$:,;?={}|^~[]\'`\\*"
+#endif
+ );
+
+ int len = cName.length();
+ if (!len)
+ return QString();
+ for (int i = 0; i < len ;++i) {
+ uchar inCh = (uchar)cName[i];
+ if (inCh >= 128 || sChars.contains(inCh))
+ {
+ newStr += QLatin1Char('%');
+ ushort c = inCh / 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newStr += QLatin1Char((char)c);
+ c = inCh % 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newStr += QLatin1Char((char)c);
+ } else {
+ newStr += QLatin1Char((char)inCh);
+ }
+ }
+ return newStr;
+ }
+
+ static bool fileExists(const Q3UrlOperator &url, const QString& name)
+ {
+ Q3Url u(url, Q3FileDialogPrivate::encodeFileName(name));
+ if (u.isLocalFile()) {
+ QFileInfo f(u.path());
+ return f.exists();
+ } else {
+ Q3NetworkProtocol *p = Q3NetworkProtocol::getNetworkProtocol(url.protocol());
+ if (p && (p->supportedOperations()&Q3NetworkProtocol::OpListChildren)) {
+ QUrlInfo ui(url.info(name.isEmpty() ? QString::fromLatin1(".") : name));
+ return ui.isValid();
+ }
+ }
+ return true;
+ }
+
+#ifndef Q_NO_CURSOR
+ bool cursorOverride; // Remember if the cursor was overridden or not.
+#endif
+};
+
+Q3FileDialogPrivate::~Q3FileDialogPrivate()
+{
+ delete modeButtons;
+}
+
+
+
+/************************************************************************
+ *
+ * Internal class QRenameEdit
+ *
+ ************************************************************************/
+
+void QRenameEdit::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_Escape)
+ emit cancelRename();
+ else
+ QLineEdit::keyPressEvent(e);
+ e->accept();
+}
+
+void QRenameEdit::focusOutEvent(QFocusEvent *)
+{
+ if (!doRenameAlreadyEmitted)
+ emitDoRename();
+}
+
+void QRenameEdit::slotReturnPressed()
+{
+ emitDoRename();
+}
+
+void QRenameEdit::emitDoRename()
+{
+ doRenameAlreadyEmitted = true;
+ emit doRename();
+ doRenameAlreadyEmitted = false;
+}
+
+/************************************************************************
+ *
+ * Internal class QFileListBox
+ *
+ ************************************************************************/
+
+QFileListBox::QFileListBox(QWidget *parent, Q3FileDialog *dlg)
+ : Q3ListBox(parent, "filelistbox"), filedialog(dlg),
+ renaming(false), renameItem(0), mousePressed(false),
+ firstMousePressEvent(true)
+{
+ changeDirTimer = new QTimer(this);
+ Q3VBox *box = new Q3VBox(viewport(), "qt_vbox");
+ box->setFrameStyle(QFrame::Box | QFrame::Plain);
+ lined = new QRenameEdit(box);
+ lined->setFixedHeight(lined->sizeHint().height());
+ box->hide();
+ box->setBackgroundRole(QPalette::Base);
+ renameTimer = new QTimer(this);
+ connect(lined, SIGNAL(doRename()),
+ this, SLOT (rename()));
+ connect(lined, SIGNAL(cancelRename()),
+ this, SLOT(cancelRename()));
+ connect(renameTimer, SIGNAL(timeout()),
+ this, SLOT(doubleClickTimeout()));
+ connect(changeDirTimer, SIGNAL(timeout()),
+ this, SLOT(changeDirDuringDrag()));
+ connect(this, SIGNAL(contentsMoving(int,int)),
+ this, SLOT(contentsMoved(int,int)));
+ viewport()->setAcceptDrops(true);
+ dragItem = 0;
+}
+
+void QFileListBox::show()
+{
+ setBackgroundRole(QPalette::Base);
+ viewport()->setBackgroundRole(QPalette::Base);
+ Q3ListBox::show();
+}
+
+void QFileListBox::keyPressEvent(QKeyEvent *e)
+{
+ if ((e->key() == Qt::Key_Enter ||
+ e->key() == Qt::Key_Return) &&
+ renaming)
+ return;
+
+ QString keyPressed = ((QKeyEvent *)e)->text().toLower();
+ QChar keyChar = keyPressed[0];
+ if (keyChar.isLetterOrNumber()) {
+ Q3ListBoxItem * i = 0;
+ if (currentItem() != -1)
+ i = item(currentItem());
+ else
+ i = firstItem();
+ if (i->next())
+ i = i->next();
+ else
+ i = firstItem();
+ while (i != item(currentItem())) {
+ QString it = text(index(i));
+ if (it[0].toLower() == keyChar) {
+ clearSelection();
+ setCurrentItem(i);
+ } else {
+ if (i->next())
+ i = i->next();
+ else {
+ if (!item(currentItem())) {
+ clearSelection();
+ break;
+ }
+ i = firstItem();
+ }
+ }
+ }
+ }
+ cancelRename();
+ Q3ListBox::keyPressEvent(e);
+}
+
+void QFileListBox::viewportMousePressEvent(QMouseEvent *e)
+{
+ pressPos = e->pos();
+ mousePressed = false;
+
+ bool didRename = renaming;
+
+ cancelRename();
+ if (!hasFocus() && !viewport()->hasFocus())
+ setFocus();
+
+ if (e->button() != Qt::LeftButton) {
+ Q3ListBox::viewportMousePressEvent(e);
+ firstMousePressEvent = false;
+ return;
+ }
+
+ int i = currentItem();
+ bool wasSelected = false;
+ if (i != -1)
+ wasSelected = item(i)->isSelected();
+ Q3ListBox::mousePressEvent(e);
+
+ Q3FileDialogPrivate::MCItem *i1 = (Q3FileDialogPrivate::MCItem*)item(currentItem());
+ if (i1)
+ mousePressed = (!((Q3FileDialogPrivate::File*)i1->i)->info.isDir())
+ || (filedialog->mode() == Q3FileDialog::Directory) || (filedialog->mode() == Q3FileDialog::DirectoryOnly);
+
+ if (itemAt(e->pos()) != item(i)) {
+ firstMousePressEvent = false;
+ return;
+ }
+
+ if (!firstMousePressEvent && !didRename && i == currentItem() && currentItem() != -1 &&
+ wasSelected && QUrlInfo(filedialog->d->url.info(QString(QLatin1Char('.')))).isWritable() && item(currentItem())->text() != QLatin1String("..")) {
+ renameTimer->start(QApplication::doubleClickInterval(), true);
+ renameItem = item(i);
+ }
+
+ firstMousePressEvent = false;
+}
+
+void QFileListBox::viewportMouseReleaseEvent(QMouseEvent *e)
+{
+ dragItem = 0;
+ Q3ListBox::viewportMouseReleaseEvent(e);
+ mousePressed = false;
+}
+
+void QFileListBox::viewportMouseDoubleClickEvent(QMouseEvent *e)
+{
+ renameTimer->stop();
+ Q3ListBox::viewportMouseDoubleClickEvent(e);
+}
+
+void QFileListBox::viewportMouseMoveEvent(QMouseEvent *e)
+{
+ if (!dragItem)
+ dragItem = itemAt(e->pos());
+ renameTimer->stop();
+#ifndef QT_NO_DRAGANDDROP
+ if ( (pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance() && mousePressed) {
+ Q3ListBoxItem *item = dragItem;
+ dragItem = 0;
+ if (item) {
+ if (!itemRect(item).contains(e->pos()))
+ return;
+ Q3UriDrag* drag = new Q3UriDrag(viewport());
+ QStringList files;
+ if (filedialog->mode() == Q3FileDialog::ExistingFiles)
+ files = filedialog->selectedFiles();
+ else
+ files = QStringList(filedialog->selectedFile());
+ drag->setFileNames(files);
+
+ if (lined->parentWidget()->isVisible())
+ cancelRename();
+
+ connect(drag, SIGNAL(destroyed()),
+ this, SLOT(dragObjDestroyed()));
+ drag->drag();
+
+ mousePressed = false;
+ }
+ } else
+#endif
+ {
+ Q3ListBox::viewportMouseMoveEvent(e);
+ }
+
+}
+
+void QFileListBox::dragObjDestroyed()
+{
+#ifndef QT_NO_DRAGANDDROP
+ //#######
+ //filedialog->rereadDir();
+#endif
+}
+
+#ifndef QT_NO_DRAGANDDROP
+void QFileListBox::viewportDragEnterEvent(QDragEnterEvent *e)
+{
+ startDragUrl = filedialog->d->url;
+ startDragDir = filedialog->dirPath();
+ currDropItem = 0;
+
+ if (!Q3UriDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+
+ QStringList l;
+ Q3UriDrag::decodeLocalFiles(e, l);
+ urls = (int)l.count();
+
+ if (acceptDrop(e->pos(), e->source())) {
+ e->accept();
+ setCurrentDropItem(e->pos());
+ } else {
+ e->ignore();
+ setCurrentDropItem(QPoint(-1, -1));
+ }
+
+ oldDragPos = e->pos();
+}
+
+void QFileListBox::viewportDragMoveEvent(QDragMoveEvent *e)
+{
+ if (acceptDrop(e->pos(), e->source())) {
+ switch (e->action()) {
+ case QDropEvent::Copy:
+ e->acceptAction();
+ break;
+ case QDropEvent::Move:
+ e->acceptAction();
+ break;
+ case QDropEvent::Link:
+ break;
+ default:
+ break;
+ }
+ if (oldDragPos != e->pos())
+ setCurrentDropItem(e->pos());
+ } else {
+ changeDirTimer->stop();
+ e->ignore();
+ setCurrentDropItem(QPoint(-1, -1));
+ }
+
+ oldDragPos = e->pos();
+}
+
+void QFileListBox::viewportDragLeaveEvent(QDragLeaveEvent *)
+{
+ changeDirTimer->stop();
+ setCurrentDropItem(QPoint(-1, -1));
+//########
+// if (startDragDir != filedialog->d->url)
+// filedialog->setUrl(startDragUrl);
+}
+
+void QFileListBox::viewportDropEvent(QDropEvent *e)
+{
+ changeDirTimer->stop();
+
+ if (!Q3UriDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+
+ Q3StrList l;
+ Q3UriDrag::decode(e, l);
+
+ bool move = e->action() == QDropEvent::Move;
+// bool supportAction = move || e->action() == QDropEvent::Copy;
+
+ Q3UrlOperator dest;
+ if (currDropItem)
+ dest = Q3UrlOperator(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text()));
+ else
+ dest = filedialog->d->url;
+ QStringList lst;
+ for (uint i = 0; i < l.count(); ++i) {
+ lst << QLatin1String(l.at(i));
+ }
+
+ filedialog->d->url.copy(lst, dest, move);
+
+ // ##### what is supportAction for?
+ e->acceptAction();
+ currDropItem = 0;
+}
+
+bool QFileListBox::acceptDrop(const QPoint &pnt, QWidget *source)
+{
+ Q3ListBoxItem *item = itemAt(pnt);
+ if (!item || (item && !itemRect(item).contains(pnt))) {
+ if (source == viewport() && startDragDir == filedialog->dirPath())
+ return false;
+ return true;
+ }
+
+ QUrlInfo fi(filedialog->d->url.info(item->text().isEmpty() ? QString::fromLatin1(".") : item->text()));
+
+ if (fi.isDir() && itemRect(item).contains(pnt))
+ return true;
+ return false;
+}
+
+void QFileListBox::setCurrentDropItem(const QPoint &pnt)
+{
+ changeDirTimer->stop();
+
+ Q3ListBoxItem *item = 0;
+ if (pnt != QPoint(-1, -1))
+ item = itemAt(pnt);
+ if (item && !QUrlInfo(filedialog->d->url.info(item->text().isEmpty() ? QString::fromLatin1(".") : item->text())).isDir())
+ item = 0;
+ if (item && !itemRect(item).contains(pnt))
+ item = 0;
+
+ currDropItem = item;
+ if (currDropItem)
+ setCurrentItem(currDropItem);
+ changeDirTimer->start(750);
+}
+#endif // QT_NO_DRAGANDDROP
+
+void QFileListBox::changeDirDuringDrag()
+{
+#ifndef QT_NO_DRAGANDDROP
+ if (!currDropItem)
+ return;
+ changeDirTimer->stop();
+ Q3Url u(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text()));
+ filedialog->setDir(u);
+ currDropItem = 0;
+#endif
+}
+
+void QFileListBox::doubleClickTimeout()
+{
+ startRename();
+ renameTimer->stop();
+}
+
+void QFileListBox::startRename(bool check)
+{
+ if (check && (!renameItem || renameItem != item(currentItem())))
+ return;
+
+ int i = currentItem();
+ setSelected(i, true);
+ QRect r = itemRect(item(i));
+ int bdr = item(i)->pixmap() ?
+ item(i)->pixmap()->width() : 16;
+ int x = r.x() + bdr;
+ int y = r.y();
+ int w = item(i)->width(this) - bdr;
+ int h = qMax(lined->height() + 2, r.height());
+ y = y + r.height() / 2 - h / 2;
+
+ lined->parentWidget()->setGeometry(x, y, w + 6, h);
+ lined->setFocus();
+ lined->setText(item(i)->text());
+ lined->selectAll();
+ lined->setFrame(false);
+ lined->parentWidget()->show();
+ viewport()->setFocusProxy(lined);
+ renaming = true;
+}
+
+void QFileListBox::clear()
+{
+ cancelRename();
+ Q3ListBox::clear();
+}
+
+void QFileListBox::rename()
+{
+ if (!lined->text().isEmpty()) {
+ QString file = currentText();
+
+ if (lined->text() != file)
+ filedialog->d->url.rename(file, lined->text());
+ }
+ cancelRename();
+}
+
+void QFileListBox::cancelRename()
+{
+ renameItem = 0;
+ lined->parentWidget()->hide();
+ viewport()->setFocusProxy(this);
+ renaming = false;
+ updateItem(currentItem());
+ if (lined->hasFocus())
+ viewport()->setFocus();
+}
+
+void QFileListBox::contentsMoved(int, int)
+{
+ changeDirTimer->stop();
+#ifndef QT_NO_DRAGANDDROP
+ setCurrentDropItem(QPoint(-1, -1));
+#endif
+}
+
+/************************************************************************
+ *
+ * Internal class QFileListView
+ *
+ ************************************************************************/
+
+Q3FileDialogQFileListView::Q3FileDialogQFileListView(QWidget *parent, Q3FileDialog *dlg)
+ : Q3ListView(parent, "qt_filedlg_listview"), renaming(false), renameItem(0),
+ filedialog(dlg), mousePressed(false),
+ firstMousePressEvent(true)
+{
+ changeDirTimer = new QTimer(this);
+ Q3VBox *box = new Q3VBox(viewport(), "qt_vbox");
+ box->setFrameStyle(QFrame::Box | QFrame::Plain);
+ lined = new QRenameEdit(box);
+ lined->setFixedHeight(lined->sizeHint().height());
+ box->hide();
+ box->setBackgroundRole(QPalette::Base);
+ renameTimer = new QTimer(this);
+ connect(lined, SIGNAL(doRename()),
+ this, SLOT (rename()));
+ connect(lined, SIGNAL(cancelRename()),
+ this, SLOT(cancelRename()));
+ header()->setMovingEnabled(false);
+ connect(renameTimer, SIGNAL(timeout()),
+ this, SLOT(doubleClickTimeout()));
+ connect(changeDirTimer, SIGNAL(timeout()),
+ this, SLOT(changeDirDuringDrag()));
+ disconnect(header(), SIGNAL(sectionClicked(int)),
+ this, SLOT(changeSortColumn(int)));
+ connect(header(), SIGNAL(sectionClicked(int)),
+ this, SLOT(changeSortColumn2(int)));
+ connect(this, SIGNAL(contentsMoving(int,int)),
+ this, SLOT(contentsMoved(int,int)));
+
+ viewport()->setAcceptDrops(true);
+ sortcolumn = 0;
+ ascending = true;
+ dragItem = 0;
+}
+
+void Q3FileDialogQFileListView::setSorting(int column, bool increasing)
+{
+ if (column == -1) {
+ Q3ListView::setSorting(column, increasing);
+ return;
+ }
+
+ sortAscending = ascending = increasing;
+ sortcolumn = column;
+ switch (column) {
+ case 0:
+ sortFilesBy = QDir::Name;
+ break;
+ case 1:
+ sortFilesBy = QDir::Size;
+ break;
+ case 3:
+ sortFilesBy = QDir::Time;
+ break;
+ default:
+ sortFilesBy = QDir::Name; // #### ???
+ break;
+ }
+
+ filedialog->resortDir();
+}
+
+void Q3FileDialogQFileListView::changeSortColumn2(int column)
+{
+ int lcol = header()->mapToLogical(column);
+ setSorting(lcol, sortcolumn == lcol ? !ascending : true);
+}
+
+void Q3FileDialogQFileListView::keyPressEvent(QKeyEvent *e)
+{
+ if ((e->key() == Qt::Key_Enter ||
+ e->key() == Qt::Key_Return) &&
+ renaming)
+ return;
+
+ QString keyPressed = e->text().toLower();
+ QChar keyChar = keyPressed[0];
+ if (keyChar.isLetterOrNumber()) {
+ Q3ListViewItem * i = 0;
+ if (currentItem())
+ i = currentItem();
+ else
+ i = firstChild();
+ if (i->nextSibling())
+ i = i->nextSibling();
+ else
+ i = firstChild();
+ while (i != currentItem()) {
+ QString it = i->text(0);
+ if (it[0].toLower() == keyChar) {
+ clearSelection();
+ ensureItemVisible(i);
+ setCurrentItem(i);
+ } else {
+ if (i->nextSibling())
+ i = i->nextSibling();
+ else
+ i = firstChild();
+ }
+ }
+ return;
+ }
+
+ cancelRename();
+ Q3ListView::keyPressEvent(e);
+}
+
+void Q3FileDialogQFileListView::viewportMousePressEvent(QMouseEvent *e)
+{
+ pressPos = e->pos();
+ mousePressed = false;
+
+ bool didRename = renaming;
+ cancelRename();
+ if (!hasFocus() && !viewport()->hasFocus())
+ setFocus();
+
+ if (e->button() != Qt::LeftButton) {
+ Q3ListView::viewportMousePressEvent(e);
+ firstMousePressEvent = false;
+ return;
+ }
+
+ Q3ListViewItem *i = currentItem();
+ Q3ListView::viewportMousePressEvent(e);
+
+ Q3FileDialogPrivate::File *i1 = (Q3FileDialogPrivate::File*)currentItem();
+ if (i1)
+ mousePressed = !i1->info.isDir() || (filedialog->mode() == Q3FileDialog::Directory) || (filedialog->mode() == Q3FileDialog::DirectoryOnly);
+
+
+ if (itemAt(e->pos()) != i ||
+ e->x() + contentsX() > columnWidth(0)) {
+ firstMousePressEvent = false;
+ return;
+ }
+
+ if (!firstMousePressEvent && !didRename && i == currentItem() && currentItem() &&
+ QUrlInfo(filedialog->d->url.info(QString(QLatin1Char('.')))).isWritable() && currentItem()->text(0) != QLatin1String("..")) {
+ renameTimer->start(QApplication::doubleClickInterval(), true);
+ renameItem = currentItem();
+ }
+
+ firstMousePressEvent = false;
+}
+
+void Q3FileDialogQFileListView::viewportMouseDoubleClickEvent(QMouseEvent *e)
+{
+ renameTimer->stop();
+ Q3ListView::viewportMouseDoubleClickEvent(e);
+}
+
+void Q3FileDialogQFileListView::viewportMouseReleaseEvent(QMouseEvent *e)
+{
+ Q3ListView::viewportMouseReleaseEvent(e);
+ mousePressed = false;
+ dragItem = 0;
+}
+
+void Q3FileDialogQFileListView::viewportMouseMoveEvent(QMouseEvent *e)
+{
+ renameTimer->stop();
+ if (!dragItem)
+ dragItem = itemAt(e->pos());
+#ifndef QT_NO_DRAGANDDROP
+ if ( (pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance() && mousePressed) {
+ Q3ListViewItem *item = dragItem;
+ dragItem = 0;
+ if (item) {
+ Q3UriDrag* drag = new Q3UriDrag(viewport());
+ QStringList files;
+ if (filedialog->mode() == Q3FileDialog::ExistingFiles)
+ files = filedialog->selectedFiles();
+ else
+ files = QStringList(filedialog->selectedFile());
+ drag->setFileNames(files);
+
+ if (lined->isVisible())
+ cancelRename();
+
+ connect(drag, SIGNAL(destroyed()),
+ this, SLOT(dragObjDestroyed()));
+ drag->drag();
+
+ mousePressed = false;
+ }
+ }
+#endif
+}
+
+void Q3FileDialogQFileListView::dragObjDestroyed()
+{
+#ifndef QT_NO_DRAGANDDROP
+ //######
+ //filedialog->rereadDir();
+#endif
+}
+
+#ifndef QT_NO_DRAGANDDROP
+void Q3FileDialogQFileListView::viewportDragEnterEvent(QDragEnterEvent *e)
+{
+ startDragUrl = filedialog->d->url;
+ startDragDir = filedialog->dirPath();
+ currDropItem = 0;
+
+ if (!Q3UriDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+
+ QStringList l;
+ Q3UriDrag::decodeLocalFiles(e, l);
+ urls = (int)l.count();
+
+ if (acceptDrop(e->pos(), e->source())) {
+ e->accept();
+ setCurrentDropItem(e->pos());
+ } else {
+ e->ignore();
+ setCurrentDropItem(QPoint(-1, -1));
+ }
+
+ oldDragPos = e->pos();
+}
+
+void Q3FileDialogQFileListView::viewportDragMoveEvent(QDragMoveEvent *e)
+{
+ if (acceptDrop(e->pos(), e->source())) {
+ if (oldDragPos != e->pos())
+ setCurrentDropItem(e->pos());
+ switch (e->action()) {
+ case QDropEvent::Copy:
+ e->acceptAction();
+ break;
+ case QDropEvent::Move:
+ e->acceptAction();
+ break;
+ case QDropEvent::Link:
+ break;
+ default:
+ break;
+ }
+ } else {
+ changeDirTimer->stop();
+ e->ignore();
+ setCurrentDropItem(QPoint(-1, -1));
+ }
+
+ oldDragPos = e->pos();
+}
+
+void Q3FileDialogQFileListView::viewportDragLeaveEvent(QDragLeaveEvent *)
+{
+ changeDirTimer->stop();
+ setCurrentDropItem(QPoint(-1, -1));
+//########
+// if (startDragDir != filedialog->d->url)
+// filedialog->setUrl(startDragUrl);
+}
+
+void Q3FileDialogQFileListView::viewportDropEvent(QDropEvent *e)
+{
+ changeDirTimer->stop();
+
+ if (!Q3UriDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+
+ QStringList l;
+ Q3UriDrag::decodeToUnicodeUris(e, l);
+
+ bool move = e->action() == QDropEvent::Move;
+// bool supportAction = move || e->action() == QDropEvent::Copy;
+
+ Q3UrlOperator dest;
+ if (currDropItem)
+ dest = Q3UrlOperator(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text(0)));
+ else
+ dest = filedialog->d->url;
+ filedialog->d->url.copy(l, dest, move);
+
+ // ##### what is supportAction for?
+ e->acceptAction();
+ currDropItem = 0;
+}
+
+bool Q3FileDialogQFileListView::acceptDrop(const QPoint &pnt, QWidget *source)
+{
+ Q3ListViewItem *item = itemAt(pnt);
+ if (!item || (item && !itemRect(item).contains(pnt))) {
+ if (source == viewport() && startDragDir == filedialog->dirPath())
+ return false;
+ return true;
+ }
+
+ QUrlInfo fi(filedialog->d->url.info(item->text(0).isEmpty() ? QString::fromLatin1(".") : item->text(0)));
+
+ if (fi.isDir() && itemRect(item).contains(pnt))
+ return true;
+ return false;
+}
+
+void Q3FileDialogQFileListView::setCurrentDropItem(const QPoint &pnt)
+{
+ changeDirTimer->stop();
+
+ Q3ListViewItem *item = itemAt(pnt);
+ if (pnt == QPoint(-1, -1))
+ item = 0;
+ if (item && !QUrlInfo(filedialog->d->url.info(item->text(0).isEmpty() ? QString::fromLatin1(".") : item->text(0))).isDir())
+ item = 0;
+
+ if (item && !itemRect(item).contains(pnt))
+ item = 0;
+
+ currDropItem = item;
+
+ if (currDropItem)
+ setCurrentItem(currDropItem);
+
+ changeDirTimer->start(750);
+}
+#endif // QT_NO_DRAGANDDROP
+
+void Q3FileDialogQFileListView::changeDirDuringDrag()
+{
+#ifndef QT_NO_DRAGANDDROP
+ if (!currDropItem)
+ return;
+ changeDirTimer->stop();
+ Q3Url u(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text(0)));
+ filedialog->setDir(u);
+ currDropItem = 0;
+#endif // QT_NO_DRAGANDDROP
+}
+
+
+void Q3FileDialogQFileListView::doubleClickTimeout()
+{
+ startRename();
+ renameTimer->stop();
+}
+
+void Q3FileDialogQFileListView::startRename(bool check)
+{
+ if (check && (!renameItem || renameItem != currentItem()))
+ return;
+
+ Q3ListViewItem *i = currentItem();
+ setSelected(i, true);
+
+ QRect r = itemRect(i);
+ int bdr = i->pixmap(0) ?
+ i->pixmap(0)->width() : 16;
+ int x = r.x() + bdr;
+ int y = r.y();
+ int w = columnWidth(0) - bdr;
+ int h = qMax(lined->height() + 2, r.height());
+ y = y + r.height() / 2 - h / 2;
+
+ lined->parentWidget()->setGeometry(x, y, w + 6, h);
+ lined->setFocus();
+ lined->setText(i->text(0));
+ lined->selectAll();
+ lined->setFrame(false);
+ lined->parentWidget()->show();
+ viewport()->setFocusProxy(lined);
+ renaming = true;
+}
+
+void Q3FileDialogQFileListView::clear()
+{
+ cancelRename();
+ Q3ListView::clear();
+}
+
+void Q3FileDialogQFileListView::rename()
+{
+ if (!lined->text().isEmpty()) {
+ QString file = currentItem()->text(0);
+
+ if (lined->text() != file)
+ filedialog->d->url.rename(file, lined->text());
+ }
+ cancelRename();
+}
+
+void Q3FileDialogQFileListView::cancelRename()
+{
+ renameItem = 0;
+ lined->parentWidget()->hide();
+ viewport()->setFocusProxy(this);
+ renaming = false;
+ if (currentItem())
+ currentItem()->repaint();
+ if (lined->hasFocus())
+ viewport()->setFocus();
+}
+
+void Q3FileDialogQFileListView::contentsMoved(int, int)
+{
+ changeDirTimer->stop();
+#ifndef QT_NO_DRAGANDDROP
+ setCurrentDropItem(QPoint(-1, -1));
+#endif
+}
+
+
+Q3FileDialogPrivate::File::~File()
+{
+ if (d->pendingItems.findRef(this))
+ d->pendingItems.removeRef(this);
+}
+
+QString Q3FileDialogPrivate::File::text(int column) const
+{
+ makeVariables();
+
+ switch(column) {
+ case 0:
+ return info.name();
+ case 1:
+ if (info.isFile()) {
+ QIODevice::Offset size = info.size();
+ return QString::number(size);
+ } else {
+ return QString::fromLatin1("");
+ }
+ case 2:
+ if (info.isFile() && info.isSymLink()) {
+ return d->symLinkToFile;
+ } else if (info.isFile()) {
+ return d->file;
+ } else if (info.isDir() && info.isSymLink()) {
+ return d->symLinkToDir;
+ } else if (info.isDir()) {
+ return d->dir;
+ } else if (info.isSymLink()) {
+ return d->symLinkToSpecial;
+ } else {
+ return d->special;
+ }
+ case 3: {
+ return info.lastModified().toString(Qt::LocalDate);
+ }
+ case 4:
+ if (info.isReadable())
+ return info.isWritable() ? d->rw : d->ro;
+ else
+ return info.isWritable() ? d->wo : d->inaccessible;
+ }
+
+ return QString::fromLatin1("<--->");
+}
+
+const QPixmap * Q3FileDialogPrivate::File::pixmap(int column) const
+{
+ if (column) {
+ return 0;
+ } else if (Q3ListViewItem::pixmap(column)) {
+ return Q3ListViewItem::pixmap(column);
+ } else if (info.isSymLink()) {
+ if (info.isFile())
+ return symLinkFileIcon;
+ else
+ return symLinkDirIcon;
+ } else if (info.isDir()) {
+ return closedFolderIcon;
+ } else if (info.isFile()) {
+ return fileIcon;
+ } else {
+ return fifteenTransparentPixels;
+ }
+}
+
+Q3FileDialogPrivate::MCItem::MCItem(Q3ListBox * lb, Q3ListViewItem * item)
+ : Q3ListBoxItem()
+{
+ i = item;
+ if (lb)
+ lb->insertItem(this);
+}
+
+Q3FileDialogPrivate::MCItem::MCItem(Q3ListBox * lb, Q3ListViewItem * item, Q3ListBoxItem *after)
+ : Q3ListBoxItem()
+{
+ i = item;
+ if (lb)
+ lb->insertItem(this, after);
+}
+
+QString Q3FileDialogPrivate::MCItem::text() const
+{
+ return i->text(0);
+}
+
+
+const QPixmap *Q3FileDialogPrivate::MCItem::pixmap() const
+{
+ return i->pixmap(0);
+}
+
+
+int Q3FileDialogPrivate::MCItem::height(const Q3ListBox * lb) const
+{
+ int hf = lb->fontMetrics().height();
+ int hp = pixmap() ? pixmap()->height() : 0;
+ return qMax(hf, hp) + 2;
+}
+
+
+int Q3FileDialogPrivate::MCItem::width(const Q3ListBox * lb) const
+{
+ QFontMetrics fm = lb->fontMetrics();
+ int w = 2;
+ if (pixmap())
+ w += pixmap()->width() + 4;
+ else
+ w += 18;
+ w += fm.width(text());
+ w += -fm.minLeftBearing();
+ w += -fm.minRightBearing();
+ w += 6;
+ return w;
+}
+
+
+void Q3FileDialogPrivate::MCItem::paint(QPainter * ptr)
+{
+ QFontMetrics fm = ptr->fontMetrics();
+
+ int h;
+
+ if (pixmap())
+ h = qMax(fm.height(), pixmap()->height()) + 2;
+ else
+ h = fm.height() + 2;
+
+ const QPixmap * pm = pixmap();
+ if (pm)
+ ptr->drawPixmap(2, 1, *pm);
+
+ ptr->drawText(pm ? pm->width() + 4 : 22, h - fm.descent() - 2,
+ text());
+}
+
+static QStringList makeFiltersList(const QString &filter)
+{
+ if (filter.isEmpty())
+ return QStringList();
+
+ int i = filter.indexOf(QLatin1String(";;"), 0);
+ QString sep(QLatin1String(";;"));
+ if (i == -1) {
+ if (filter.contains(QLatin1Char('\n'))) {
+ sep = QLatin1Char('\n');
+ i = filter.indexOf(sep);
+ }
+ }
+
+ return QStringList::split(sep, filter);
+}
+
+/*!
+ \class Q3FileDialog
+ \brief The Q3FileDialog class provides dialogs that allow users to select files or directories.
+
+ \compat
+
+ The Q3FileDialog class enables a user to traverse their file system in
+ order to select one or many files or a directory.
+
+ The easiest way to create a Q3FileDialog is to use the static
+ functions. On Windows, these static functions will call the native
+ Windows file dialog and on Mac OS X, these static function will call
+ the native Mac OS X file dialog.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 0
+
+ In the above example, a modal Q3FileDialog is created using a static
+ function. The startup directory is set to "/home". The file filter
+ is set to "Images (*.png *.xpm *.jpg)". The parent of the file dialog
+ is set to \e this and it is given the identification name - "open file
+ dialog". The caption at the top of file dialog is set to "Choose a
+ file". If you want to use multiple filters, separate each one with
+ \e two semicolons, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 1
+
+ You can create your own Q3FileDialog without using the static
+ functions. By calling setMode(), you can set what can be returned by
+ the Q3FileDialog.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 2
+
+ In the above example, the mode of the file dialog is set to \l
+ AnyFile, meaning that the user can select any file, or even specify a
+ file that doesn't exist. This mode is useful for creating a "File Save
+ As" file dialog. Use \l ExistingFile if the user must select an
+ existing file or \l Directory if only a directory may be selected.
+ (See the \l Q3FileDialog::Mode enum for the complete list of modes.)
+
+ You can retrieve the dialog's mode with mode(). Use setFilter() to set
+ the dialog's file filter, e.g.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 3
+
+ In the above example, the filter is set to "Images (*.png *.xpm
+ *.jpg)", this means that only files with the extension \c png, \c xpm
+ or \c jpg will be shown in the Q3FileDialog. You can apply
+ several filters by using setFilters() and add additional filters with
+ addFilter(). Use setSelectedFilter() to select one of the filters
+ you've given as the file dialog's default filter. Whenever the user
+ changes the filter the filterSelected() signal is emitted.
+
+ The file dialog has two view modes, Q3FileDialog::List which simply
+ lists file and directory names and Q3FileDialog::Detail which
+ displays additional information alongside each name, e.g. file size,
+ modification date, etc. Set the mode with setViewMode().
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 4
+
+ The last important function you will need to use when creating your
+ own file dialog is selectedFile().
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 5
+
+ In the above example, a modal file dialog is created and shown. If
+ the user clicked OK, then the file they selected is put in \c
+ fileName.
+
+ If you are using the \l ExistingFiles mode then you will need to use
+ selectedFiles() which will return the selected files in a QStringList.
+
+ The dialog's working directory can be set with setDir(). The display
+ of hidden files is controlled with setShowHiddenFiles(). The dialog
+ can be forced to re-read the directory with rereadDir() and re-sort
+ the directory with resortDir(). All the files in the current directory
+ can be selected with selectAll().
+
+ \section1 Creating and using preview widgets
+
+ There are two kinds of preview widgets that can be used with
+ Q3FileDialogs: \e content preview widgets and \e information preview
+ widgets. They are created and used in the same way except that the
+ function names differ, e.g. setContentsPreview() and setInfoPreview().
+
+ A preview widget is a widget that is placed inside a Q3FileDialog so
+ that the user can see either the contents of the file, or information
+ about the file.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 6
+
+ In the above snippet, we create a preview widget which inherits from
+ QLabel and Q3FilePreview. File preview widgets \e must inherit from
+ Q3FilePreview.
+
+ Inside the class we reimplement Q3FilePreview::previewUrl(), this is
+ where we determine what happens when a file is selected. In the
+ above example we only show a preview of the file if it is a valid
+ pixmap. Here's how to make a file dialog use a preview widget:
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 7
+
+ The first line creates an instance of our preview widget. We then
+ create our file dialog and call setContentsPreviewEnabled(true),
+ this tell the file dialog to preview the contents of the currently
+ selected file. We then call setContentsPreview() -- note that we pass
+ the same preview widget twice. Finally, before showing the file
+ dialog, we call setPreviewMode() setting the mode to \e Contents which
+ will show the contents preview of the file that the user has selected.
+
+ If you create another preview widget that is used for displaying
+ information about a file, create it in the same way as the contents
+ preview widget and call setInfoPreviewEnabled(), and
+ setInfoPreview(). Then the user will be able to switch between the
+ two preview modes.
+
+ For more information about creating a Q3FilePreview widget see
+ \l{Q3FilePreview}.
+*/
+
+
+/*! \enum Q3FileDialog::Mode
+
+ This enum is used to indicate what the user may select in the file
+ dialog, i.e. what the dialog will return if the user clicks OK.
+
+ \value AnyFile The name of a file, whether it exists or not.
+ \value ExistingFile The name of a single existing file.
+ \value Directory The name of a directory. Both files and directories
+ are displayed.
+ \value DirectoryOnly The name of a directory. The file dialog will only display directories.
+ \value ExistingFiles The names of zero or more existing files.
+
+ See setMode().
+*/
+
+/*!
+ \enum Q3FileDialog::ViewMode
+
+ This enum describes the view mode of the file dialog, i.e. what
+ information about each file will be displayed.
+
+ \value List Display file and directory names with icons.
+ \value Detail Display file and directory names with icons plus
+ additional information, such as file size and modification date.
+
+ See setViewMode().
+*/
+
+/*!
+ \enum Q3FileDialog::PreviewMode
+
+ This enum describes the preview mode of the file dialog.
+
+ \value NoPreview No preview is shown at all.
+ \value Contents Show a preview of the contents of the current file
+ using the contents preview widget.
+ \value Info Show information about the current file using the
+ info preview widget.
+
+ See setPreviewMode(), setContentsPreview() and setInfoPreview().
+*/
+
+/*!
+ \fn void Q3FileDialog::detailViewSelectionChanged()
+ \internal
+*/
+
+/*!
+ \fn void Q3FileDialog::listBoxSelectionChanged()
+ \internal
+*/
+
+extern const char qt3_file_dialog_filter_reg_exp[] = "([a-zA-Z0-9]*)\\(([a-zA-Z0-9_.*? +;#\\[\\]]*)\\)$";
+
+/*!
+ Constructs a file dialog called \a name, with the parent, \a parent.
+ If \a modal is true then the file dialog is modal; otherwise it is
+ modeless.
+*/
+
+Q3FileDialog::Q3FileDialog(QWidget *parent, const char *name, bool modal)
+ : QDialog(parent, name, modal,
+ (modal ?
+ (Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu) : Qt::WindowFlags(0)))
+{
+ init();
+ d->mode = ExistingFile;
+ d->types->insertItem(tr("All Files (*)"));
+ d->cursorOverride = false;
+ emit dirEntered(d->url.dirPath());
+ rereadDir();
+}
+
+
+/*!
+ Constructs a file dialog called \a name with the parent, \a parent.
+ If \a modal is true then the file dialog is modal; otherwise it is
+ modeless.
+
+ If \a dirName is specified then it will be used as the dialog's
+ working directory, i.e. it will be the directory that is shown when
+ the dialog appears. If \a filter is specified it will be used as the
+ dialog's file filter.
+
+*/
+
+Q3FileDialog::Q3FileDialog(const QString& dirName, const QString & filter,
+ QWidget *parent, const char *name, bool modal)
+ : QDialog(parent, name, modal,
+ (modal ? (Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu)
+ : Qt::WindowFlags(0)))
+{
+ init();
+ d->mode = ExistingFile;
+ rereadDir();
+ Q3UrlOperator u(dirName);
+ if (!dirName.isEmpty() && (!u.isLocalFile() || QDir(dirName).exists()))
+ setSelection(dirName);
+ else if (workingDirectory && !workingDirectory->isEmpty())
+ setDir(*workingDirectory);
+
+ if (!filter.isEmpty()) {
+ setFilters(filter);
+ if (!dirName.isEmpty()) {
+ int dotpos = dirName.indexOf(QLatin1Char('.'), 0, Qt::CaseInsensitive);
+ if (dotpos != -1) {
+ for (int b=0 ; b<d->types->count() ; b++) {
+ if (d->types->text(b).contains(dirName.right(dirName.length() - dotpos))) {
+ d->types->setCurrentItem(b);
+ setFilter(d->types->text(b));
+ return;
+ }
+ }
+ }
+ }
+ } else {
+ d->types->insertItem(tr("All Files (*)"));
+ }
+}
+
+
+/*!
+ \internal
+ Initializes the file dialog.
+*/
+
+void Q3FileDialog::init()
+{
+ setSizeGripEnabled(true);
+ d = new Q3FileDialogPrivate();
+ d->mode = AnyFile;
+ d->last = 0;
+ d->lastEFSelected = 0;
+ d->moreFiles = 0;
+ d->infoPreview = false;
+ d->contentsPreview = false;
+ d->hadDotDot = false;
+ d->ignoreNextKeyPress = false;
+ d->progressDia = 0;
+ d->checkForFilter = false;
+ d->ignoreNextRefresh = false;
+ d->ignoreStop = false;
+ d->mimeTypeTimer = new QTimer(this);
+ d->cursorOverride = false;
+ connect(d->mimeTypeTimer, SIGNAL(timeout()),
+ this, SLOT(doMimeTypeLookup()));
+
+ d->url = Q3UrlOperator(toRootIfNotExists( QDir::currentDirPath() ));
+ d->oldUrl = d->url;
+ d->currListChildren = 0;
+
+ connect(&d->url, SIGNAL(start(Q3NetworkOperation*)),
+ this, SLOT(urlStart(Q3NetworkOperation*)));
+ connect(&d->url, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(urlFinished(Q3NetworkOperation*)));
+ connect(&d->url, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ this, SLOT(insertEntry(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)));
+ connect(&d->url, SIGNAL(removed(Q3NetworkOperation*)),
+ this, SLOT(removeEntry(Q3NetworkOperation*)));
+ connect(&d->url, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+ this, SLOT(createdDirectory(QUrlInfo,Q3NetworkOperation*)));
+ connect(&d->url, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ this, SLOT(itemChanged(Q3NetworkOperation*)));
+ connect(&d->url, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ this, SLOT(dataTransferProgress(int,int,Q3NetworkOperation*)));
+
+ nameEdit = new QLineEdit(this, "name/filter editor");
+ nameEdit->setMaxLength(255); //_POSIX_MAX_PATH
+ connect(nameEdit, SIGNAL(textChanged(QString)),
+ this, SLOT(fileNameEditDone()));
+ nameEdit->installEventFilter(this);
+
+ d->splitter = new QSplitter(this, "qt_splitter");
+
+ d->stack = new Q3WidgetStack(d->splitter, "files and more files");
+
+ d->splitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+
+ files = new Q3FileDialogQFileListView(d->stack, this);
+ QFontMetrics fm = fontMetrics();
+ files->addColumn(tr("Name"));
+ files->addColumn(tr("Size"));
+ files->setColumnAlignment(1, Qt::AlignRight);
+ files->addColumn(tr("Type"));
+ files->addColumn(tr("Date"));
+ files->addColumn(tr("Attributes"));
+ files->header()->setStretchEnabled(true, 0);
+
+ files->setMinimumSize(50, 25 + 2*fm.lineSpacing());
+
+ connect(files, SIGNAL(selectionChanged()),
+ this, SLOT(detailViewSelectionChanged()));
+ connect(files, SIGNAL(currentChanged(Q3ListViewItem*)),
+ this, SLOT(updateFileNameEdit(Q3ListViewItem*)));
+ connect(files, SIGNAL(doubleClicked(Q3ListViewItem*)),
+ this, SLOT(selectDirectoryOrFile(Q3ListViewItem*)));
+ connect(files, SIGNAL(returnPressed(Q3ListViewItem*)),
+ this, SLOT(selectDirectoryOrFile(Q3ListViewItem*)));
+ connect(files, SIGNAL(contextMenuRequested(Q3ListViewItem*,QPoint,int)),
+ this, SLOT(popupContextMenu(Q3ListViewItem*,QPoint,int)));
+
+ files->installEventFilter(this);
+ files->viewport()->installEventFilter(this);
+
+ d->moreFiles = new QFileListBox(d->stack, this);
+ d->moreFiles->setRowMode(Q3ListBox::FitToHeight);
+ d->moreFiles->setVariableWidth(true);
+
+ connect(d->moreFiles, SIGNAL(selected(Q3ListBoxItem*)),
+ this, SLOT(selectDirectoryOrFile(Q3ListBoxItem*)));
+ connect(d->moreFiles, SIGNAL(selectionChanged()),
+ this, SLOT(listBoxSelectionChanged()));
+ connect(d->moreFiles, SIGNAL(highlighted(Q3ListBoxItem*)),
+ this, SLOT(updateFileNameEdit(Q3ListBoxItem*)));
+ connect(d->moreFiles, SIGNAL(contextMenuRequested(Q3ListBoxItem*,QPoint)),
+ this, SLOT(popupContextMenu(Q3ListBoxItem*,QPoint)));
+
+ d->moreFiles->installEventFilter(this);
+ d->moreFiles->viewport()->installEventFilter(this);
+
+ okB = new QPushButton(tr("&OK"), this, "OK"); //### Or "Save (see other "OK")
+ okB->setDefault(true);
+ okB->setEnabled(false);
+ connect(okB, SIGNAL(clicked()), this, SLOT(okClicked()));
+ cancelB = new QPushButton(tr("Cancel") , this, "Cancel");
+ connect(cancelB, SIGNAL(clicked()), this, SLOT(cancelClicked()));
+
+ d->paths = new Q3ComboBox(true, this, "directory history/editor");
+ d->paths->setDuplicatesEnabled(false);
+ d->paths->setInsertionPolicy(Q3ComboBox::NoInsertion);
+ makeVariables();
+
+ QFileInfoList rootDrives = QDir::drives();
+ for (int i = 0; i < rootDrives.size(); ++i) {
+ QFileInfo fi = rootDrives.at(i);
+ d->paths->insertItem(*openFolderIcon, fi.absFilePath());
+ }
+
+ if (QDir::homeDirPath().size()) {
+ if (!d->paths->listBox()->findItem(QDir::homeDirPath()))
+ d->paths->insertItem(*openFolderIcon, QDir::homeDirPath());
+ }
+
+ connect(d->paths, SIGNAL(activated(QString)),
+ this, SLOT(setDir(QString)));
+
+ d->paths->installEventFilter(this);
+ QObjectList ol = d->paths->queryList("QLineEdit");
+ if (ol.size())
+ ol.at(0)->installEventFilter(this);
+
+ d->geometryDirty = true;
+ d->types = new QComboBox(true, this, "file types");
+ d->types->setDuplicatesEnabled(false);
+ d->types->setEditable(false);
+ connect(d->types, SIGNAL(activated(QString)),
+ this, SLOT(setFilter(QString)));
+ connect(d->types, SIGNAL(activated(QString)),
+ this, SIGNAL(filterSelected(QString)));
+
+ d->pathL = new QLabel(d->paths, tr("Look &in:"), this, "qt_looin_lbl");
+ d->fileL = new QLabel(nameEdit, tr("File &name:"), this, "qt_filename_lbl");
+ d->typeL = new QLabel(d->types, tr("File &type:"), this, "qt_filetype_lbl");
+
+ d->goBack = new QToolButton(this, "go back");
+ d->goBack->setEnabled(false);
+ d->goBack->setFocusPolicy(Qt::TabFocus);
+ connect(d->goBack, SIGNAL(clicked()), this, SLOT(goBack()));
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add(d->goBack, tr("Back"));
+#endif
+ d->goBack->setIconSet(*goBackIcon);
+
+ d->cdToParent = new QToolButton(this, "cd to parent");
+ d->cdToParent->setFocusPolicy(Qt::TabFocus);
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add(d->cdToParent, tr("One directory up"));
+#endif
+ d->cdToParent->setIconSet(*cdToParentIcon);
+ connect(d->cdToParent, SIGNAL(clicked()),
+ this, SLOT(cdUpClicked()));
+
+ d->newFolder = new QToolButton(this, "new folder");
+ d->newFolder->setFocusPolicy(Qt::TabFocus);
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add(d->newFolder, tr("Create New Folder"));
+#endif
+ d->newFolder->setIconSet(*newFolderIcon);
+ connect(d->newFolder, SIGNAL(clicked()),
+ this, SLOT(newFolderClicked()));
+
+ d->modeButtons = new Q3ButtonGroup(0, "invisible group");
+ connect(d->modeButtons, SIGNAL(destroyed()),
+ this, SLOT(modeButtonsDestroyed()));
+ d->modeButtons->setExclusive(true);
+ connect(d->modeButtons, SIGNAL(clicked(int)),
+ d->stack, SLOT(raiseWidget(int)));
+ connect(d->modeButtons, SIGNAL(clicked(int)),
+ this, SLOT(changeMode(int)));
+
+ d->mcView = new QToolButton(this, "mclistbox view");
+ d->mcView->setFocusPolicy(Qt::TabFocus);
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add(d->mcView, tr("List View"));
+#endif
+ d->mcView->setIconSet(*multiColumnListViewIcon);
+ d->mcView->setToggleButton(true);
+ d->stack->addWidget(d->moreFiles, d->modeButtons->insert(d->mcView));
+ d->detailView = new QToolButton(this, "list view");
+ d->detailView->setFocusPolicy(Qt::TabFocus);
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add(d->detailView, tr("Detail View"));
+#endif
+ d->detailView->setIconSet(*detailViewIcon);
+ d->detailView->setToggleButton(true);
+ d->stack->addWidget(files, d->modeButtons->insert(d->detailView));
+
+ d->previewInfo = new QToolButton(this, "preview info view");
+ d->previewInfo->setFocusPolicy(Qt::TabFocus);
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add(d->previewInfo, tr("Preview File Info"));
+#endif
+ d->previewInfo->setIconSet(*previewInfoViewIcon);
+ d->previewInfo->setToggleButton(true);
+ d->modeButtons->insert(d->previewInfo);
+
+ d->previewContents = new QToolButton(this, "preview info view");
+ if (!qstrcmp(style()->className(), "QWindowsStyle"))
+ {
+ d->goBack->setAutoRaise(true);
+ d->cdToParent->setAutoRaise(true);
+ d->newFolder->setAutoRaise(true);
+ d->mcView->setAutoRaise(true);
+ d->detailView->setAutoRaise(true);
+ d->previewInfo->setAutoRaise(true);
+ d->previewContents->setAutoRaise(true);
+ }
+ d->previewContents->setFocusPolicy(Qt::TabFocus);
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add(d->previewContents, tr("Preview File Contents"));
+#endif
+ d->previewContents->setIconSet(*previewContentsViewIcon);
+ d->previewContents->setToggleButton(true);
+ d->modeButtons->insert(d->previewContents);
+
+ connect(d->detailView, SIGNAL(clicked()),
+ d->moreFiles, SLOT(cancelRename()));
+ connect(d->detailView, SIGNAL(clicked()),
+ files, SLOT(cancelRename()));
+ connect(d->mcView, SIGNAL(clicked()),
+ d->moreFiles, SLOT(cancelRename()));
+ connect(d->mcView, SIGNAL(clicked()),
+ files, SLOT(cancelRename()));
+
+ d->stack->raiseWidget(d->moreFiles);
+ d->mcView->setOn(true);
+
+ QHBoxLayout *lay = new QHBoxLayout(this);
+ lay->setMargin(6);
+ d->leftLayout = new QHBoxLayout(lay, 5);
+ d->topLevelLayout = new QVBoxLayout((QWidget*)0, 5);
+ lay->addLayout(d->topLevelLayout, 1);
+
+ QHBoxLayout * h;
+
+ d->preview = new Q3WidgetStack(d->splitter, "qt_preview");
+
+ d->infoPreviewWidget = new QWidget(d->preview, "qt_preview_info");
+ d->contentsPreviewWidget = new QWidget(d->preview, "qt_preview_contents");
+ d->infoPreviewer = d->contentsPreviewer = 0;
+
+ h = new QHBoxLayout(0);
+ d->buttonLayout = h;
+ d->topLevelLayout->addLayout(h);
+ h->addWidget(d->pathL);
+ h->addSpacing(8);
+ h->addWidget(d->paths);
+ h->addSpacing(8);
+ if (d->goBack)
+ h->addWidget(d->goBack);
+ h->addWidget(d->cdToParent);
+ h->addSpacing(2);
+ h->addWidget(d->newFolder);
+ h->addSpacing(4);
+ h->addWidget(d->mcView);
+ h->addWidget(d->detailView);
+ h->addWidget(d->previewInfo);
+ h->addWidget(d->previewContents);
+
+ d->topLevelLayout->addWidget(d->splitter);
+
+ h = new QHBoxLayout();
+ d->topLevelLayout->addLayout(h);
+ h->addWidget(d->fileL);
+ h->addWidget(nameEdit);
+ h->addSpacing(15);
+ h->addWidget(okB);
+
+ h = new QHBoxLayout();
+ d->topLevelLayout->addLayout(h);
+ h->addWidget(d->typeL);
+ h->addWidget(d->types);
+ h->addSpacing(15);
+ h->addWidget(cancelB);
+
+ d->rightLayout = new QHBoxLayout(lay, 5);
+ d->topLevelLayout->setStretchFactor(d->mcView, 1);
+ d->topLevelLayout->setStretchFactor(files, 1);
+
+ updateGeometries();
+
+ if (d->goBack) {
+ setTabOrder(d->paths, d->goBack);
+ setTabOrder(d->goBack, d->cdToParent);
+ } else {
+ setTabOrder(d->paths, d->cdToParent);
+ }
+ setTabOrder(d->cdToParent, d->newFolder);
+ setTabOrder(d->newFolder, d->mcView);
+ setTabOrder(d->mcView, d->detailView);
+ setTabOrder(d->detailView, d->moreFiles);
+ setTabOrder(d->moreFiles, files);
+ setTabOrder(files, nameEdit);
+ setTabOrder(nameEdit, d->types);
+ setTabOrder(d->types, okB);
+ setTabOrder(okB, cancelB);
+
+ d->rw = tr("Read-write");
+ d->ro = tr("Read-only");
+ d->wo = tr("Write-only");
+ d->inaccessible = tr("Inaccessible");
+
+ d->symLinkToFile = tr("Symlink to File");
+ d->symLinkToDir = tr("Symlink to Directory");
+ d->symLinkToSpecial = tr("Symlink to Special");
+ d->file = tr("File");
+ d->dir = tr("Dir");
+ d->special = tr("Special");
+
+ if (lastWidth == 0) {
+ QRect screen = QApplication::desktop()->screenGeometry(pos());
+ if (screen.width() < 1024 || screen.height() < 768) {
+ resize(qMin(screen.width(), 420), qMin(screen.height(), 236));
+ } else {
+ QSize s = files->sizeHint();
+ s = QSize(s.width() + 300, s.height() + 82);
+
+ if (s.width() * 3 > screen.width() * 2)
+ s.setWidth(screen.width() * 2 / 3);
+
+ if (s.height() * 3 > screen.height() * 2)
+ s.setHeight(screen.height() * 2 / 3);
+ else if (s.height() * 3 < screen.height())
+ s.setHeight(screen.height() / 3);
+
+ resize(s);
+ }
+ updateLastSize(this);
+ } else {
+ resize(lastWidth, lastHeight);
+ }
+
+ if (detailViewMode) {
+ d->stack->raiseWidget(files);
+ d->mcView->setOn(false);
+ d->detailView->setOn(true);
+ }
+
+ d->preview->hide();
+ nameEdit->setFocus();
+
+ connect(nameEdit, SIGNAL(returnPressed()),
+ this, SLOT(fileNameEditReturnPressed()));
+}
+
+/*!
+ \internal
+*/
+
+void Q3FileDialog::fileNameEditReturnPressed()
+{
+ d->oldUrl = d->url;
+ if (!isDirectoryMode(d->mode)) {
+ okClicked();
+ } else {
+ d->currentFileName.clear();
+ if (nameEdit->text().isEmpty()) {
+ emit fileSelected(selectedFile());
+ accept();
+ } else {
+ QUrlInfo f;
+ Q3FileDialogPrivate::File * c
+ = (Q3FileDialogPrivate::File *)files->currentItem();
+ if (c && files->isSelected(c))
+ f = c->info;
+ else
+ f = QUrlInfo(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text()));
+ if (f.isDir()) {
+ setUrl(Q3UrlOperator(d->url,
+ Q3FileDialogPrivate::encodeFileName(nameEdit->text() + QLatin1Char('/'))));
+ d->checkForFilter = true;
+ trySetSelection(true, d->url, true);
+ d->checkForFilter = false;
+ }
+ }
+ nameEdit->setText(QString());
+ }
+}
+
+/*!
+ \internal
+ Update the info and content preview widgets to display \a u.
+*/
+
+void Q3FileDialog::updatePreviews(const Q3Url &u)
+{
+ if (d->infoPreviewer)
+ d->infoPreviewer->previewUrl(u);
+ if (d->contentsPreviewer)
+ d->contentsPreviewer->previewUrl(u);
+}
+
+/*!
+ \internal
+ Changes the preview mode to the mode specified at \a id.
+*/
+
+void Q3FileDialog::changeMode(int id)
+{
+ if (!d->infoPreview && !d->contentsPreview)
+ return;
+
+ QAbstractButton*btn = d->modeButtons->find(id);
+ if (!btn)
+ return;
+
+ if (btn == d->previewContents && !d->contentsPreview)
+ return;
+ if (btn == d->previewInfo && !d->infoPreview)
+ return;
+
+ if (btn != d->previewContents && btn != d->previewInfo) {
+ d->preview->hide();
+ } else {
+ if (files->currentItem())
+ updatePreviews(Q3Url(d->url, files->currentItem()->text(0)));
+ if (btn == d->previewInfo)
+ d->preview->raiseWidget(d->infoPreviewWidget);
+ else
+ d->preview->raiseWidget(d->contentsPreviewWidget);
+ d->preview->show();
+ }
+}
+
+/*!
+ Destroys the file dialog.
+*/
+
+Q3FileDialog::~Q3FileDialog()
+{
+ // since clear might call setContentsPos which would emit
+ // a signal and thus cause a recompute of sizes...
+ files->blockSignals(true);
+ d->moreFiles->blockSignals(true);
+ files->clear();
+ d->moreFiles->clear();
+ d->moreFiles->blockSignals(false);
+ files->blockSignals(false);
+
+#ifndef QT_NO_CURSOR
+ if (d->cursorOverride)
+ QApplication::restoreOverrideCursor();
+#endif
+
+ delete d;
+ d = 0;
+}
+
+
+/*!
+ \property Q3FileDialog::selectedFile
+
+ \brief the name of the selected file
+
+ If a file was selected selectedFile contains the file's name including
+ its absolute path; otherwise selectedFile is empty.
+
+ \sa QString::isEmpty(), selectedFiles, selectedFilter
+*/
+
+QString Q3FileDialog::selectedFile() const
+{
+ QString s = d->currentFileName;
+ // remove the protocol because we do not want to encode it...
+ QString prot = Q3Url(s).protocol();
+ if (!prot.isEmpty()) {
+ prot += QLatin1Char(':');
+ s.remove(0, prot.length());
+ }
+ Q3Url u(prot + Q3FileDialogPrivate::encodeFileName(s));
+ if (u.isLocalFile()) {
+ QString s = u.toString();
+ if (s.left(5) == QLatin1String("file:"))
+ s.remove((uint)0, 5);
+ return s;
+ }
+ return d->currentFileName;
+}
+
+/*!
+ \property Q3FileDialog::selectedFilter
+
+ \brief the filter which the user has selected in the file dialog
+
+ \sa filterSelected(), selectedFiles, selectedFile
+*/
+
+QString Q3FileDialog::selectedFilter() const
+{
+ return d->types->currentText();
+}
+
+/*! \overload
+
+ Sets the current filter selected in the file dialog to the
+ \a{n}-th filter in the filter list.
+
+ \sa filterSelected(), selectedFilter(), selectedFiles(), selectedFile()
+*/
+
+void Q3FileDialog::setSelectedFilter(int n)
+{
+ d->types->setCurrentItem(n);
+ QString f = d->types->currentText();
+ QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp));
+ int index = r.indexIn(f);
+ if (index >= 0)
+ f = r.cap(2);
+ d->url.setNameFilter(f);
+ rereadDir();
+}
+
+/*!
+ Sets the current filter selected in the file dialog to the first
+ one that contains the text \a mask.
+*/
+
+void Q3FileDialog::setSelectedFilter(const QString& mask)
+{
+ int n;
+
+ for (n = 0; n < d->types->count(); n++) {
+ if (d->types->text(n).contains(mask, Qt::CaseInsensitive)) {
+ d->types->setCurrentItem(n);
+ QString f = mask;
+ QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp));
+ int index = r.indexIn(f);
+ if (index >= 0)
+ f = r.cap(2);
+ d->url.setNameFilter(f);
+ rereadDir();
+ return;
+ }
+ }
+}
+
+/*!
+ \property Q3FileDialog::selectedFiles
+
+ \brief the list of selected files
+
+ If one or more files are selected, selectedFiles contains their
+ names including their absolute paths. If no files are selected or
+ the mode isn't ExistingFiles selectedFiles is an empty list.
+
+ It is more convenient to use selectedFile() if the mode is
+ \l ExistingFile, \c Directory or \c DirectoryOnly.
+
+ Note that if you want to iterate over the list, you should
+ iterate over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 8
+
+ \sa selectedFile, selectedFilter, QList::isEmpty()
+*/
+
+QStringList Q3FileDialog::selectedFiles() const
+{
+ QStringList lst;
+
+ if (mode() == ExistingFiles) {
+ QStringList selectedLst;
+ QString selectedFiles = nameEdit->text();
+ if (selectedFiles.lastIndexOf(QLatin1Char('\"')) == -1) {
+ //probably because Enter was pressed on the nameEdit, so we have one file
+ //not in "" but raw
+ selectedLst.append(selectedFiles);
+ } else {
+ selectedFiles.truncate(selectedFiles.lastIndexOf(QLatin1Char('\"')));
+ selectedLst = selectedLst.split(QLatin1String("\" "), selectedFiles);
+ }
+ for (QStringList::Iterator it = selectedLst.begin(); it != selectedLst.end(); ++it) {
+ Q3Url u;
+ if ((*it)[0] == QLatin1Char('\"')) {
+ u = Q3Url(d->url, Q3FileDialogPrivate::encodeFileName((*it).mid(1)));
+ } else {
+ u = Q3Url(d->url, Q3FileDialogPrivate::encodeFileName((*it)));
+ }
+ if (u.isLocalFile()) {
+ QString s = u.toString();
+ if (s.left(5) == QLatin1String("file:"))
+ s.remove((uint)0, 5);
+ lst << s;
+ } else {
+ lst << u.toString();
+ }
+ }
+ }
+
+ return lst;
+}
+
+/*!
+ Sets the default selection to \a filename. If \a filename is
+ absolute, setDir() is also called to set the file dialog's working
+ directory to the filename's directory.
+
+ \omit
+ Only for external use. Not useful inside Q3FileDialog.
+ \endomit
+*/
+
+void Q3FileDialog::setSelection(const QString & filename)
+{
+ d->oldUrl = d->url;
+ QString nf = d->url.nameFilter();
+ if (Q3Url::isRelativeUrl(filename))
+ d->url = Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(filename));
+ else
+ d->url = Q3UrlOperator(filename);
+ d->url.setNameFilter(nf);
+ d->checkForFilter = true;
+ bool isDirOk;
+ bool isDir = d->url.isDir(&isDirOk);
+ if (!isDirOk)
+ isDir = d->url.path().right(1) == QString(QLatin1Char('/'));
+ if (!isDir) {
+ Q3UrlOperator u(d->url);
+ d->url.setPath(d->url.dirPath());
+ trySetSelection(false, u, true);
+ d->ignoreNextRefresh = true;
+ nameEdit->selectAll();
+ rereadDir();
+ emit dirEntered(d->url.dirPath());
+ } else {
+ if (!d->url.path().isEmpty() &&
+ d->url.path().right(1) != QString(QLatin1Char('/'))) {
+ QString p = d->url.path();
+ p += QLatin1Char('/');
+ d->url.setPath(p);
+ }
+ trySetSelection(true, d->url, false);
+ rereadDir();
+ emit dirEntered(d->url.dirPath());
+ nameEdit->setText(QString::fromLatin1(""));
+ }
+ d->checkForFilter = false;
+}
+
+/*!
+ \property Q3FileDialog::dirPath
+
+ \brief the file dialog's working directory
+
+ \sa dir(), setDir()
+*/
+
+QString Q3FileDialog::dirPath() const
+{
+ return d->url.dirPath();
+}
+
+
+/*!
+
+ Sets the filter used in the file dialog to \a newFilter.
+
+ If \a newFilter contains a pair of parentheses containing one or more
+ of "anything*something" separated by spaces or by
+ semicolons then only the text contained in the parentheses is used as
+ the filter. This means that these calls are all equivalent:
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 9
+
+ \sa setFilters()
+*/
+
+void Q3FileDialog::setFilter(const QString & newFilter)
+{
+ if (newFilter.isEmpty())
+ return;
+ QString f = newFilter;
+ QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp));
+ int index = r.indexIn(f);
+ if (index >= 0)
+ f = r.cap(2);
+ d->url.setNameFilter(f);
+ if (d->types->count() == 1) {
+ d->types->clear();
+ d->types->insertItem(newFilter);
+ } else {
+ for (int i = 0; i < d->types->count(); ++i) {
+ if (d->types->text(i).left(newFilter.length()) == newFilter ||
+ d->types->text(i).left(f.length()) == f) {
+ d->types->setCurrentItem(i);
+ break;
+ }
+ }
+ }
+ rereadDir();
+}
+
+
+/*! \overload
+ Sets the file dialog's working directory to \a pathstr.
+
+ \sa dir()
+*/
+
+void Q3FileDialog::setDir(const QString & pathstr)
+{
+ QString dr = pathstr;
+ if (dr.isEmpty())
+ return;
+
+#if defined(Q_OS_UNIX)
+ if (dr.length() && dr[0] == QLatin1Char('~')) {
+ int i = 0;
+ while(i < (int)dr.length() && dr[i] != QLatin1Char('/'))
+ i++;
+ Q3CString user;
+ if (i == 1) {
+#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
+
+# ifndef _POSIX_LOGIN_NAME_MAX
+# define _POSIX_LOGIN_NAME_MAX 9
+# endif
+
+ char name[_POSIX_LOGIN_NAME_MAX];
+ if (::getlogin_r(name, _POSIX_LOGIN_NAME_MAX) == 0)
+ user = name;
+ else
+#else
+ user = ::getlogin();
+ if (user.isEmpty())
+#endif
+ user = qgetenv("LOGNAME");
+ } else
+ user = dr.mid(1, i-1).local8Bit();
+ dr = dr.mid(i, dr.length());
+ struct passwd *pw;
+#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_FREEBSD) && !defined(Q_OS_OPENBSD)
+ struct passwd mt_pw;
+ char buffer[2048];
+ if (::getpwnam_r(user, &mt_pw, buffer, 2048, &pw) == 0 && pw == &mt_pw)
+#else
+ pw = ::getpwnam(user);
+ if (pw)
+#endif
+ dr.prepend(QString::fromLocal8Bit(pw->pw_dir));
+ }
+#endif
+
+ setUrl(dr);
+}
+
+/*!
+ Returns the current directory shown in the file dialog.
+
+ The ownership of the QDir pointer is transferred to the caller, so
+ it must be deleted by the caller when no longer required.
+
+ \sa setDir()
+*/
+
+const QDir *Q3FileDialog::dir() const
+{
+ if (d->url.isLocalFile())
+ return new QDir(d->url.path());
+ else
+ return 0;
+}
+
+/*!
+ Sets the file dialog's working directory to \a dir.
+ \sa dir()
+*/
+
+void Q3FileDialog::setDir(const QDir &dir)
+{
+ d->oldUrl = d->url;
+ QString nf(d->url.nameFilter());
+ d->url = dir.canonicalPath();
+ d->url.setNameFilter(nf);
+ QUrlInfo i(d->url.info(nameEdit->text().isEmpty()? QString::fromLatin1(".") : nameEdit->text()));
+ d->checkForFilter = true;
+ trySetSelection(i.isDir(), Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(nameEdit->text())), false);
+ d->checkForFilter = false;
+ rereadDir();
+ emit dirEntered(d->url.path());
+}
+
+/*!
+ Sets the file dialog's working directory to the directory specified at \a url.
+
+ \sa url()
+*/
+
+void Q3FileDialog::setUrl(const Q3UrlOperator &url)
+{
+ d->oldUrl = d->url;
+ QString nf = d->url.nameFilter();
+
+ QString operatorPath = url.toString(false, false);
+ if (Q3Url::isRelativeUrl(operatorPath)) {
+ d->url = Q3Url(d->url, operatorPath);
+ } else {
+ d->url = url;
+ }
+ d->url.setNameFilter(nf);
+
+ d->checkForFilter = true;
+ if (!d->url.isDir()) {
+ Q3UrlOperator u = d->url;
+ d->url.setPath(d->url.dirPath());
+ trySetSelection(false, u, false);
+ rereadDir();
+ emit dirEntered(d->url.dirPath());
+ QString fn = u.fileName();
+ nameEdit->setText(fn);
+ } else {
+ trySetSelection(true, d->url, false);
+ rereadDir();
+ emit dirEntered(d->url.dirPath());
+ }
+ d->checkForFilter = false;
+}
+
+/*!
+ \property Q3FileDialog::showHiddenFiles
+
+ \brief whether hidden files are shown in the file dialog
+
+ The default is false, i.e. don't show hidden files.
+*/
+
+void Q3FileDialog::setShowHiddenFiles(bool s)
+{
+ if (s == bShowHiddenFiles)
+ return;
+
+ bShowHiddenFiles = s;
+ rereadDir();
+}
+
+bool Q3FileDialog::showHiddenFiles() const
+{
+ return bShowHiddenFiles;
+}
+
+/*!
+ Rereads the current directory shown in the file dialog.
+
+ The only time you will need to call this function is if the contents of
+ the directory change and you wish to refresh the file dialog to reflect
+ the change.
+
+ \sa resortDir()
+*/
+
+void Q3FileDialog::rereadDir()
+{
+#ifndef QT_NO_CURSOR
+ if (!d->cursorOverride) {
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ d->cursorOverride = true;
+ }
+#endif
+ d->pendingItems.clear();
+ if (d->mimeTypeTimer->isActive())
+ d->mimeTypeTimer->stop();
+ d->currListChildren = d->url.listChildren();
+#ifndef QT_NO_CURSOR
+ if (d->cursorOverride) {
+ QApplication::restoreOverrideCursor();
+ d->cursorOverride = false;
+ }
+#endif
+}
+
+
+/*!
+ \fn void Q3FileDialog::fileHighlighted(const QString& file)
+
+ This signal is emitted when the user highlights the given \a file,
+ i.e. makes it the current file.
+
+ \sa fileSelected(), filesSelected()
+*/
+
+/*!
+ \fn void Q3FileDialog::fileSelected(const QString& file)
+
+ This signal is emitted when the user selects the given \a file.
+
+ \sa filesSelected(), fileHighlighted(), selectedFile()
+*/
+
+/*!
+ \fn void Q3FileDialog::filesSelected(const QStringList& files)
+
+ This signal is emitted when the user selects the given \a files in \e
+ ExistingFiles mode.
+
+ \sa fileSelected(), fileHighlighted(), selectedFiles()
+*/
+
+/*!
+ \fn void Q3FileDialog::dirEntered(const QString& directory)
+
+ This signal is emitted when the user enters the given \a directory.
+
+ \sa dir()
+*/
+
+/*!
+ \fn void Q3FileDialog::filterSelected(const QString& filter)
+
+ This signal is emitted when the user selects the given \a filter.
+
+ \sa selectedFilter()
+*/
+
+extern bool qt_resolve_symlinks; // defined in q3url.cpp
+extern Q_GUI_EXPORT bool qt_use_native_dialogs; //qtgui
+
+/*!
+ This is a convenience static function that returns an existing file
+ selected by the user. If the user pressed Cancel, it returns a null
+ string.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 10
+
+ The function creates a modal file dialog called \a name, with
+ parent, \a parent. If a parent is not 0, the dialog will be shown
+ centered over the parent.
+
+ The file dialog's working directory will be set to \a startWith. If \a
+ startWith includes a file name, the file will be selected. The filter
+ is set to \a filter so that only those files which match the filter
+ are shown. The filter selected is set to \a selectedFilter. The parameters
+ \a startWith, \a selectedFilter and \a filter may be an empty string.
+
+ The dialog's caption is set to \a caption. If \a caption is not
+ specified then a default caption will be used.
+
+ Under Windows and Mac OS X, this static function will use the native
+ file dialog and not a Q3FileDialog, unless the style of the application
+ is set to something other than the native style (Note that on Windows the
+ dialog will spin a blocking modal event loop that will not dispatch any
+ QTimers and if parent is not 0 then it will position the dialog just under
+ the parent's title bar).
+
+ Under Unix/X11, the normal behavior of the file dialog is to resolve
+ and follow symlinks. For example, if /usr/tmp is a symlink to /var/tmp,
+ the file dialog will change to /var/tmp after entering /usr/tmp.
+ If \a resolveSymlinks is false, the file dialog will treat
+ symlinks as regular directories.
+
+ \sa getOpenFileNames(), getSaveFileName(), getExistingDirectory()
+*/
+
+QString Q3FileDialog::getOpenFileName(const QString & startWith,
+ const QString& filter,
+ QWidget *parent, const char* name,
+ const QString& caption,
+ QString *selectedFilter,
+ bool resolveSymlinks)
+{
+ bool save_qt_resolve_symlinks = qt_resolve_symlinks;
+ qt_resolve_symlinks = resolveSymlinks;
+
+ QStringList filters;
+ if (!filter.isEmpty())
+ filters = makeFiltersList(filter);
+
+ makeVariables();
+ QString initialSelection;
+ //### Problem with the logic here: If a startWith is given and a file
+ // with that name exists in D->URL, the box will be opened at D->URL instead of
+ // the last directory used ('workingDirectory').
+ //
+ // hm... isn't that problem exactly the documented behaviour? the
+ // documented behaviour sounds meaningful.
+ if (!startWith.isEmpty()) {
+ Q3UrlOperator u(Q3FileDialogPrivate::encodeFileName(startWith));
+ if (u.isLocalFile() && QFileInfo(u.path()).isDir()) {
+ *workingDirectory = startWith;
+ } else {
+ if (u.isLocalFile()) {
+ QFileInfo fi(u.dirPath());
+ if (fi.exists()) {
+ *workingDirectory = u.dirPath();
+ initialSelection = u.fileName();
+ }
+ } else {
+ *workingDirectory = u.toString();
+ initialSelection.clear();
+ }
+ }
+ }
+
+ if (workingDirectory->isNull())
+ *workingDirectory = toRootIfNotExists( QDir::currentDirPath() );
+
+#if defined(Q_WS_WIN)
+ if (qt_use_native_dialogs && qobject_cast<QWindowsStyle *>(qApp->style()))
+ return winGetOpenFileName(initialSelection, filter, workingDirectory,
+ parent, name, caption, selectedFilter);
+#elif defined(Q_WS_MAC)
+ if(qt_use_native_dialogs && qobject_cast<QMacStyle *>(qApp->style())) {
+ QStringList files = macGetOpenFileNames(filter, startWith.isEmpty() ? 0 : workingDirectory,
+ parent, name, caption, selectedFilter, false);
+ return files.isEmpty() ? QString() : files.first().normalized(QString::NormalizationForm_C);
+ }
+#endif
+
+ Q3FileDialog *dlg = new Q3FileDialog(*workingDirectory, QString(), parent, name ? name : "qt_filedlg_gofn", true);
+
+ if (!caption.isNull())
+ dlg->setWindowTitle(caption);
+ else
+ dlg->setWindowTitle(Q3FileDialog::tr("Open"));
+
+ dlg->setFilters(filters);
+ if (selectedFilter)
+ dlg->setFilter(*selectedFilter);
+ dlg->setMode(Q3FileDialog::ExistingFile);
+ QString result;
+ if (!initialSelection.isEmpty())
+ dlg->setSelection(initialSelection);
+ if (dlg->exec() == QDialog::Accepted) {
+ result = dlg->selectedFile();
+ *workingDirectory = dlg->d->url;
+ if (selectedFilter)
+ *selectedFilter = dlg->selectedFilter();
+ }
+ delete dlg;
+
+ qt_resolve_symlinks = save_qt_resolve_symlinks;
+
+ return result;
+}
+
+/*!
+ This is a convenience static function that will return a file name
+ selected by the user. The file does not have to exist.
+
+ It creates a modal file dialog called \a name, with parent, \a parent.
+ If a parent is not 0, the dialog will be shown centered over the
+ parent.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 11
+
+ The file dialog's working directory will be set to \a startWith. If \a
+ startWith includes a file name, the file will be selected. The filter
+ is set to \a filter so that only those files which match the filter
+ are shown. The filter selected is set to \a selectedFilter. The parameters
+ \a startWith, \a selectedFilter and \a filter may be an empty string.
+
+ The dialog's caption is set to \a caption. If \a caption is not
+ specified then a default caption will be used.
+
+ Under Windows and Mac OS X, this static function will use the native
+ file dialog and not a Q3FileDialog, unless the style of the application
+ is set to something other than the native style. (Note that on Windows the
+ dialog will spin a blocking modal event loop that will not dispatch any
+ QTimers and if parent is not 0 then it will position the dialog just under
+ the parent's title bar. And on the Mac the filter argument is ignored).
+
+ Under Unix/X11, the normal behavior of the file dialog is to resolve
+ and follow symlinks. For example, if /usr/tmp is a symlink to /var/tmp,
+ the file dialog will change to /var/tmp after entering /usr/tmp.
+ If \a resolveSymlinks is false, the file dialog will treat
+ symlinks as regular directories.
+
+ \sa getOpenFileName(), getOpenFileNames(), getExistingDirectory()
+*/
+
+QString Q3FileDialog::getSaveFileName(const QString & startWith,
+ const QString& filter,
+ QWidget *parent, const char* name,
+ const QString& caption,
+ QString *selectedFilter,
+ bool resolveSymlinks)
+{
+ bool save_qt_resolve_symlinks = qt_resolve_symlinks;
+ qt_resolve_symlinks = resolveSymlinks;
+
+ QStringList filters;
+ if (!filter.isEmpty())
+ filters = makeFiltersList(filter);
+
+ makeVariables();
+ QString initialSelection;
+ if (!startWith.isEmpty()) {
+ Q3UrlOperator u(Q3FileDialogPrivate::encodeFileName(startWith));
+ if (u.isLocalFile() && QFileInfo(u.path()).isDir()) {
+ *workingDirectory = startWith;
+ } else {
+ if (u.isLocalFile()) {
+ QFileInfo fi(u.dirPath());
+ if (fi.exists()) {
+ *workingDirectory = u.dirPath();
+ initialSelection = u.fileName();
+ }
+ } else {
+ *workingDirectory = u.toString();
+ initialSelection.clear();
+ }
+ }
+ }
+
+ if (workingDirectory->isNull())
+ *workingDirectory = toRootIfNotExists( QDir::currentDirPath() );
+
+#if defined(Q_WS_WIN)
+ if (qt_use_native_dialogs && qobject_cast<QWindowsStyle *>(qApp->style()))
+ return winGetSaveFileName(initialSelection, filter, workingDirectory,
+ parent, name, caption, selectedFilter);
+#elif defined(Q_WS_MAC)
+ if(qt_use_native_dialogs && qobject_cast<QMacStyle *>(qApp->style()))
+ return macGetSaveFileName(initialSelection.isNull() ? startWith : initialSelection,
+ filter, startWith.isEmpty() ? 0 : workingDirectory, parent, name,
+ caption, selectedFilter).normalized(QString::NormalizationForm_C);
+#endif
+
+ Q3FileDialog *dlg = new Q3FileDialog(*workingDirectory, QString(), parent, name ? name : "qt_filedlg_gsfn", true);
+
+ if (!caption.isNull())
+ dlg->setWindowTitle(caption);
+ else
+ dlg->setWindowTitle(Q3FileDialog::tr("Save As"));
+
+ QString result;
+ dlg->setFilters(filters);
+ if (selectedFilter)
+ dlg->setFilter(*selectedFilter);
+ dlg->setMode(Q3FileDialog::AnyFile);
+ if (!initialSelection.isEmpty())
+ dlg->setSelection(initialSelection);
+ if (dlg->exec() == QDialog::Accepted) {
+ result = dlg->selectedFile();
+ *workingDirectory = dlg->d->url;
+ if (selectedFilter)
+ *selectedFilter = dlg->selectedFilter();
+ }
+ delete dlg;
+
+ qt_resolve_symlinks = save_qt_resolve_symlinks;
+
+ return result;
+}
+
+/*!
+ \internal
+ Activated when the "OK" button is clicked.
+*/
+
+void Q3FileDialog::okClicked()
+{
+ QString fn(nameEdit->text());
+
+#if defined(Q_WS_WIN)
+ QFileInfo fi(d->url.path() + fn);
+ if (fi.isSymLink()) {
+ nameEdit->setText(fi.symLinkTarget());
+ }
+#endif
+
+ if (fn.contains(QLatin1Char('*'))) {
+ addFilter(fn);
+ nameEdit->blockSignals(true);
+ nameEdit->setText(QString::fromLatin1(""));
+ nameEdit->blockSignals(false);
+ return;
+ }
+
+ *workingDirectory = d->url;
+ detailViewMode = files->isVisible();
+ updateLastSize(this);
+
+ if (isDirectoryMode(d->mode)) {
+ QUrlInfo f(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text()));
+ if (f.isDir()) {
+ d->currentFileName = d->url;
+ if (d->currentFileName.right(1) != QString(QLatin1Char('/')))
+ d->currentFileName += QLatin1Char('/');
+ if (f.name() != QString(QLatin1Char('.')))
+ d->currentFileName += f.name();
+ accept();
+ return;
+ }
+ // Since it's not a directory and we clicked ok, we
+ // don't really want to do anything else
+ return;
+ }
+
+ // if we're in multi-selection mode and something is selected,
+ // accept it and be done.
+ if (mode() == ExistingFiles) {
+ if (! nameEdit->text().isEmpty()) {
+ QStringList sf = selectedFiles();
+ bool isdir = false;
+ if (sf.count() == 1) {
+ Q3UrlOperator u(d->url, sf[0]);
+ bool ok;
+ isdir = u.isDir(&ok) && ok;
+ }
+ if (!isdir) {
+ emit filesSelected(sf);
+ accept();
+ return;
+ }
+ }
+ }
+
+ if (mode() == AnyFile) {
+ Q3UrlOperator u(d->url, Q3FileDialogPrivate::encodeFileName(nameEdit->text()));
+ if (!u.isDir()) {
+ d->currentFileName = u;
+ emit fileSelected(selectedFile());
+ accept();
+ return;
+ }
+ }
+
+ if (mode() == ExistingFile) {
+ if (!Q3FileDialogPrivate::fileExists(d->url, nameEdit->text()))
+ return;
+ }
+
+ // If selection is valid, return it, else try
+ // using selection as a directory to change to.
+ if (!d->currentFileName.isNull() && !d->currentFileName.contains(QLatin1Char('*'))) {
+ emit fileSelected(selectedFile());
+ accept();
+ } else {
+ QUrlInfo f;
+ Q3FileDialogPrivate::File * c
+ = (Q3FileDialogPrivate::File *)files->currentItem();
+ Q3FileDialogPrivate::MCItem * m
+ = (Q3FileDialogPrivate::MCItem *)d->moreFiles->item(d->moreFiles->currentItem());
+ if ((c && files->isVisible() && files->hasFocus())
+ || (m && d->moreFiles->isVisible())) {
+ if (c && files->isVisible())
+ f = c->info;
+ else
+ f = ((Q3FileDialogPrivate::File*)m->i)->info;
+ } else {
+ f = QUrlInfo(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text()));
+ }
+ if (f.isDir()) {
+#if defined(Q_WS_WIN)
+ if (f.isSymLink())
+ setUrl(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(fn + QLatin1Char('/'))));
+ else
+#else
+ setUrl(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(f.name() + QLatin1Char('/'))));
+#endif
+ d->checkForFilter = true;
+ trySetSelection(true, d->url, true);
+ d->checkForFilter = false;
+ } else {
+ if (!nameEdit->text().contains(QLatin1Char('/')) &&
+ !nameEdit->text().contains(QLatin1String("\\"))
+#if defined(Q_OS_WIN32)
+ && nameEdit->text()[1] != QLatin1Char(':')
+#endif
+ )
+ addFilter(nameEdit->text());
+ else if (nameEdit->text()[0] == QLatin1Char('/') ||
+ nameEdit->text()[0] == QLatin1Char('\\')
+#if defined(Q_OS_WIN32)
+ || nameEdit->text()[1] == QLatin1Char(':')
+#endif
+ )
+ setDir(nameEdit->text());
+ else if (nameEdit->text().left(3) == QLatin1String("../") || nameEdit->text().left(3) == QLatin1String("..\\"))
+ setDir(Q3Url(d->url.toString(), Q3FileDialogPrivate::encodeFileName(nameEdit->text())).toString());
+ }
+ nameEdit->setText(QLatin1String(""));
+ }
+}
+
+/*!
+ \internal
+ Activated when the "Filter" button is clicked.
+*/
+
+void Q3FileDialog::filterClicked()
+{
+ // unused
+}
+
+/*!
+ \internal
+ Activated when the "Cancel" button is clicked.
+*/
+
+void Q3FileDialog::cancelClicked()
+{
+ *workingDirectory = d->url;
+ detailViewMode = files->isVisible();
+ updateLastSize(this);
+ reject();
+}
+
+
+/*!\reimp
+*/
+
+void Q3FileDialog::resizeEvent(QResizeEvent * e)
+{
+ QDialog::resizeEvent(e);
+ updateGeometries();
+}
+
+/*
+ \internal
+ The only correct way to try to set currentFileName
+*/
+bool Q3FileDialog::trySetSelection(bool isDir, const Q3UrlOperator &u, bool updatelined)
+{
+ if (!isDir && !u.path().isEmpty() && u.path().right(1) == QString(QLatin1Char('/')))
+ isDir = true;
+ if (u.fileName().contains(QLatin1Char('*')) && d->checkForFilter) {
+ QString fn(u.fileName());
+ if (fn.contains(QLatin1Char('*'))) {
+ addFilter(fn);
+ d->currentFileName.clear();
+ d->url.setFileName(QString());
+ nameEdit->setText(QString::fromLatin1(""));
+ return false;
+ }
+ }
+
+ if (isDir && d->preview && d->preview->isVisible())
+ updatePreviews(u);
+
+ QString old = d->currentFileName;
+
+ if (isDirectoryMode(mode())) {
+ if (isDir)
+ d->currentFileName = u;
+ else
+ d->currentFileName.clear();
+ } else if (!isDir && mode() == ExistingFiles) {
+ d->currentFileName = u;
+ } else if (!isDir || (mode() == AnyFile && !isDir)) {
+ d->currentFileName = u;
+ } else {
+ d->currentFileName.clear();
+ }
+ if (updatelined && !d->currentFileName.isEmpty()) {
+ // If the selection is valid, or if its a directory, allow OK.
+ if (!d->currentFileName.isNull() || isDir) {
+ if (u.fileName() != QLatin1String("..")) {
+ QString fn = u.fileName();
+ nameEdit->setText(fn);
+ } else {
+ nameEdit->setText(QLatin1String(""));
+ }
+ } else
+ nameEdit->setText(QString::fromLatin1(""));
+ }
+
+ if (!d->currentFileName.isNull() || isDir) {
+ okB->setEnabled(true);
+ } else if (!isDirectoryMode(d->mode)) {
+ okB->setEnabled(false);
+ }
+
+ if (d->currentFileName.length() && old != d->currentFileName)
+ emit fileHighlighted(selectedFile());
+
+ return !d->currentFileName.isNull();
+}
+
+
+/*! Make sure the minimum and maximum sizes of everything are sane.
+*/
+
+void Q3FileDialog::updateGeometries()
+{
+ if (!d || !d->geometryDirty)
+ return;
+
+ d->geometryDirty = false;
+
+ QSize r, t;
+
+ // we really should use QSize::expandedTo()
+#define RM r.setWidth(qMax(r.width(),t.width())); \
+r.setHeight(qMax(r.height(),t.height()))
+
+ // labels first
+ r = d->pathL->sizeHint();
+ t = d->fileL->sizeHint();
+ RM;
+ t = d->typeL->sizeHint();
+ RM;
+ d->pathL->setFixedSize(d->pathL->sizeHint());
+ d->fileL->setFixedSize(r);
+ d->typeL->setFixedSize(r);
+
+ // single-line input areas
+ r = d->paths->sizeHint();
+ t = nameEdit->sizeHint();
+ RM;
+ t = d->types->sizeHint();
+ RM;
+ r.setWidth(t.width() * 2 / 3);
+ t.setWidth(QWIDGETSIZE_MAX);
+ t.setHeight(r.height());
+ d->paths->setMinimumSize(r);
+ d->paths->setMaximumSize(t);
+ nameEdit->setMinimumSize(r);
+ nameEdit->setMaximumSize(t);
+ d->types->setMinimumSize(r);
+ d->types->setMaximumSize(t);
+
+ // buttons on top row
+ r = QSize(0, d->paths->minimumSize().height());
+ t = QSize(21, 20);
+ RM;
+ if (r.height()+1 > r.width())
+ r.setWidth(r.height()+1);
+ if (d->goBack)
+ d->goBack->setFixedSize(r);
+ d->cdToParent->setFixedSize(r);
+ d->newFolder->setFixedSize(r);
+ d->mcView->setFixedSize(r);
+ d->detailView->setFixedSize(r);
+
+ QAbstractButton *b = 0;
+ if (!d->toolButtons.isEmpty()) {
+ for (b = d->toolButtons.first(); b; b = d->toolButtons.next())
+ b->setFixedSize(b->sizeHint().width(), r.height());
+ }
+
+ if (d->infoPreview) {
+ d->previewInfo->show();
+ d->previewInfo->setFixedSize(r);
+ } else {
+ d->previewInfo->hide();
+ d->previewInfo->setFixedSize(QSize(0, 0));
+ }
+
+ if (d->contentsPreview) {
+ d->previewContents->show();
+ d->previewContents->setFixedSize(r);
+ } else {
+ d->previewContents->hide();
+ d->previewContents->setFixedSize(QSize(0, 0));
+ }
+
+ // open/save, cancel
+ r = QSize(75, 20);
+ t = okB->sizeHint();
+ RM;
+ t = cancelB->sizeHint();
+ RM;
+
+ okB->setFixedSize(r);
+ cancelB->setFixedSize(r);
+
+ d->topLevelLayout->activate();
+
+#undef RM
+}
+
+
+/*! Updates the file name edit box to \a newItem in the file dialog
+ when the cursor moves in the listview.
+*/
+
+void Q3FileDialog::updateFileNameEdit(Q3ListViewItem * newItem)
+{
+ if (!newItem)
+ return;
+
+ if (mode() == ExistingFiles) {
+ detailViewSelectionChanged();
+ Q3Url u(d->url, Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)files->currentItem())->info.name()));
+ QFileInfo fi(u.toString(false, false));
+ if (!fi.isDir())
+ emit fileHighlighted(u.toString(false, false));
+ } else if (files->isSelected(newItem)) {
+ Q3FileDialogPrivate::File * i = (Q3FileDialogPrivate::File *)newItem;
+ if (i && i->i && !i->i->isSelected()) {
+ d->moreFiles->blockSignals(true);
+ d->moreFiles->setSelected(i->i, true);
+ d->moreFiles->blockSignals(false);
+ }
+ // Encode the filename in case it had any special characters in it
+ QString encFile = Q3FileDialogPrivate::encodeFileName(newItem->text(0));
+ trySetSelection(i->info.isDir(), Q3UrlOperator(d->url, encFile), true);
+ }
+}
+
+void Q3FileDialog::detailViewSelectionChanged()
+{
+ if (d->mode != ExistingFiles)
+ return;
+
+ nameEdit->clear();
+ QString str;
+ Q3ListViewItem * i = files->firstChild();
+ d->moreFiles->blockSignals(true);
+ while(i) {
+ if (d->moreFiles && isVisible()) {
+ Q3FileDialogPrivate::File *f = (Q3FileDialogPrivate::File *)i;
+ if (f->i && f->i->isSelected() != i->isSelected())
+ d->moreFiles->setSelected(f->i, i->isSelected());
+ }
+ if (i->isSelected() && !((Q3FileDialogPrivate::File *)i)->info.isDir())
+ str += QString::fromLatin1("\"%1\" ").arg(i->text(0));
+ i = i->nextSibling();
+ }
+ d->moreFiles->blockSignals(false);
+ nameEdit->setText(str);
+ nameEdit->setCursorPosition(str.length());
+ okB->setEnabled(true);
+ if (d->preview && d->preview->isVisible() && files->currentItem()) {
+ Q3Url u = Q3Url(d->url, Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)files->currentItem())->info.name()));
+ updatePreviews(u);
+ }
+}
+
+void Q3FileDialog::listBoxSelectionChanged()
+{
+ if (d->mode != ExistingFiles)
+ return;
+
+ if (d->ignoreNextRefresh) {
+ d->ignoreNextRefresh = false;
+ return;
+ }
+
+ nameEdit->clear();
+ QString str;
+ Q3ListBoxItem * i = d->moreFiles->item(0);
+ Q3ListBoxItem * j = 0;
+ int index = 0;
+ files->blockSignals(true);
+ while(i) {
+ Q3FileDialogPrivate::MCItem *mcitem = (Q3FileDialogPrivate::MCItem *)i;
+ if (files && isVisible()) {
+ if (mcitem->i->isSelected() != mcitem->isSelected()) {
+ files->setSelected(mcitem->i, mcitem->isSelected());
+
+ // What happens here is that we want to emit signal highlighted for
+ // newly added items. But Q3ListBox apparently emits selectionChanged even
+ // when a user clicks on the same item twice. So, basically emulate the behaivor
+ // we have in the "Details" view which only emits highlighted the first time we
+ // click on the item. Perhaps at some point we should have a call to
+ // updateFileNameEdit(Q3ListViewItem) which also emits fileHighlighted() for
+ // ExistingFiles. For better or for worse, this clones the behaivor of the
+ // "Details" view quite well.
+ if (mcitem->isSelected() && i != d->lastEFSelected) {
+ Q3Url u(d->url, Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)(mcitem)->i)->info.name()));
+ d->lastEFSelected = i;
+ emit fileHighlighted(u.toString(false, false));
+ }
+ }
+ }
+ if (d->moreFiles->isSelected(i)
+ && !((Q3FileDialogPrivate::File*)(mcitem)->i)->info.isDir()) {
+ str += QString::fromLatin1("\"%1\" ").arg(i->text());
+ if (j == 0)
+ j = i;
+ }
+ i = d->moreFiles->item(++index);
+ }
+
+ files->blockSignals(false);
+ nameEdit->setText(str);
+ nameEdit->setCursorPosition(str.length());
+ okB->setEnabled(true);
+ if (d->preview && d->preview->isVisible() && j) {
+ Q3Url u = Q3Url(d->url,
+ Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)((Q3FileDialogPrivate::MCItem*)j)->i)->info.name()));
+ updatePreviews(u);
+ }
+}
+
+/*! \overload */
+
+void Q3FileDialog::updateFileNameEdit(Q3ListBoxItem * newItem)
+{
+ if (!newItem)
+ return;
+ Q3FileDialogPrivate::MCItem * i = (Q3FileDialogPrivate::MCItem *)newItem;
+ if (i->i) {
+ i->i->listView()->setSelected(i->i, i->isSelected());
+ updateFileNameEdit(i->i);
+ }
+}
+
+
+/*! Updates the dialog when the file name edit changes. */
+
+void Q3FileDialog::fileNameEditDone()
+{
+ QUrlInfo f(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text()));
+ if (mode() != Q3FileDialog::ExistingFiles) {
+ Q3UrlOperator u(d->url, Q3FileDialogPrivate::encodeFileName(nameEdit->text()));
+ trySetSelection(f.isDir(), u, false);
+ if (d->preview && d->preview->isVisible())
+ updatePreviews(u);
+ }
+}
+
+
+
+/*! This private slot reacts to double-clicks in the list view. The item that
+was double-clicked is specified in \a newItem */
+
+void Q3FileDialog::selectDirectoryOrFile(Q3ListViewItem * newItem)
+{
+
+ *workingDirectory = d->url;
+ detailViewMode = files->isVisible();
+ updateLastSize(this);
+
+ if (!newItem)
+ return;
+
+ if (d->url.isLocalFile()) {
+ QFileInfo fi(d->url.path() + newItem->text(0));
+#if defined(Q_WS_WIN)
+ if (fi.isSymLink()) {
+ nameEdit->setText(fi.symLinkTarget());
+ okClicked();
+ return;
+ }
+#endif
+ }
+
+ Q3FileDialogPrivate::File * i = (Q3FileDialogPrivate::File *)newItem;
+
+ QString oldName = nameEdit->text();
+ if (i->info.isDir()) {
+ setUrl(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(i->info.name()) + QLatin1Char('/')));
+ if (isDirectoryMode(mode())) {
+ QUrlInfo f (d->url.info(QString::fromLatin1(".")));
+ trySetSelection(f.isDir(), d->url, true);
+ }
+ } else if (newItem->isSelectable() &&
+ trySetSelection(i->info.isDir(), Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(i->info.name())), true)) {
+ if (!isDirectoryMode(mode())) {
+ if (mode() == ExistingFile) {
+ if (Q3FileDialogPrivate::fileExists(d->url, nameEdit->text())) {
+ emit fileSelected(selectedFile());
+ accept();
+ }
+ } else {
+ emit fileSelected(selectedFile());
+ accept();
+ }
+ }
+ } else if (isDirectoryMode(d->mode)) {
+ d->currentFileName = d->url;
+ accept();
+ }
+ if (!oldName.isEmpty() && !isDirectoryMode(mode()))
+ nameEdit->setText(oldName);
+}
+
+
+void Q3FileDialog::selectDirectoryOrFile(Q3ListBoxItem * newItem)
+{
+ if (!newItem)
+ return;
+
+ Q3FileDialogPrivate::MCItem * i = (Q3FileDialogPrivate::MCItem *)newItem;
+ if (i->i) {
+ i->i->listView()->setSelected(i->i, i->isSelected());
+ selectDirectoryOrFile(i->i);
+ }
+}
+
+
+void Q3FileDialog::popupContextMenu(Q3ListViewItem *item, const QPoint &p,
+ int)
+{
+ if (item) {
+ files->setCurrentItem(item);
+ files->setSelected(item, true);
+ }
+
+ PopupAction action;
+ popupContextMenu(item ? item->text(0) : QString(), true, action, p);
+
+ if (action == PA_Open)
+ selectDirectoryOrFile(item);
+ else if (action == PA_Rename)
+ files->startRename(false);
+ else if (action == PA_Delete)
+ deleteFile(item ? item->text(0) : QString());
+ else if (action == PA_Reload)
+ rereadDir();
+ else if (action == PA_Hidden) {
+ bShowHiddenFiles = !bShowHiddenFiles;
+ rereadDir();
+ } else if (action == PA_SortName) {
+ sortFilesBy = (int)QDir::Name;
+ sortAscending = true;
+ resortDir();
+ } else if (action == PA_SortSize) {
+ sortFilesBy = (int)QDir::Size;
+ sortAscending = true;
+ resortDir();
+ } else if (action == PA_SortDate) {
+ sortFilesBy = (int)QDir::Time;
+ sortAscending = true;
+ resortDir();
+ } else if (action == PA_SortUnsorted) {
+ sortFilesBy = (int)QDir::Unsorted;
+ sortAscending = true;
+ resortDir();
+ }
+
+}
+
+void Q3FileDialog::popupContextMenu(Q3ListBoxItem *item, const QPoint & p)
+{
+ PopupAction action;
+ popupContextMenu(item ? item->text() : QString(), false, action, p);
+
+ if (action == PA_Open)
+ selectDirectoryOrFile(item);
+ else if (action == PA_Rename)
+ d->moreFiles->startRename(false);
+ else if (action == PA_Delete)
+ deleteFile(item->text());
+ else if (action == PA_Reload)
+ rereadDir();
+ else if (action == PA_Hidden) {
+ bShowHiddenFiles = !bShowHiddenFiles;
+ rereadDir();
+ } else if (action == PA_SortName) {
+ sortFilesBy = (int)QDir::Name;
+ sortAscending = true;
+ resortDir();
+ } else if (action == PA_SortSize) {
+ sortFilesBy = (int)QDir::Size;
+ sortAscending = true;
+ resortDir();
+ } else if (action == PA_SortDate) {
+ sortFilesBy = (int)QDir::Time;
+ sortAscending = true;
+ resortDir();
+ } else if (action == PA_SortUnsorted) {
+ sortFilesBy = (int)QDir::Unsorted;
+ sortAscending = true;
+ resortDir();
+ }
+}
+
+void Q3FileDialog::popupContextMenu(const QString &filename, bool,
+ PopupAction &action, const QPoint &p)
+{
+ action = PA_Cancel;
+
+ bool glob = filename.isEmpty();
+
+ Q3PopupMenu m(0, "file dialog context menu");
+ m.setCheckable(true);
+
+ if (!glob) {
+ QString okt;
+ if (QUrlInfo(d->url.info(filename.isEmpty() ? QString::fromLatin1(".") : fileName)).isDir()) {
+ okt = tr("&Open");
+ } else {
+ if (mode() == AnyFile)
+ okt = tr("&Save");
+ else
+ okt = tr("&Open");
+ }
+ int ok = m.insertItem(okt);
+
+ m.insertSeparator();
+ int rename = m.insertItem(tr("&Rename"));
+ int del = m.insertItem(tr("&Delete"));
+
+ if (filename.isEmpty() || !QUrlInfo(d->url.info(filename)).isWritable() ||
+ filename == QLatin1String("..")) {
+ if (filename.isEmpty() || !QUrlInfo(d->url.info(filename)).isReadable())
+ m.setItemEnabled(ok, false);
+ m.setItemEnabled(rename, false);
+ m.setItemEnabled(del, false);
+ }
+
+ m.move(p);
+ int res = m.exec(QCursor::pos(), -1);
+
+ if (res == ok)
+ action = PA_Open;
+ else if (res == rename)
+ action = PA_Rename;
+ else if (res == del)
+ action = PA_Delete;
+ } else {
+ int reload = m.insertItem(tr("R&eload"));
+
+ Q3PopupMenu m2(0, "sort menu");
+
+ int sname = m2.insertItem(tr("Sort by &Name"));
+ //int stype = m2.insertItem(tr("Sort by &Type"));
+ int ssize = m2.insertItem(tr("Sort by &Size"));
+ int sdate = m2.insertItem(tr("Sort by &Date"));
+ m2.insertSeparator();
+ int sunsorted = m2.insertItem(tr("&Unsorted"));
+
+ //m2.setItemEnabled(stype, false);
+
+ if (sortFilesBy == (int)QDir::Name)
+ m2.setItemChecked(sname, true);
+ else if (sortFilesBy == (int)QDir::Size)
+ m2.setItemChecked(ssize, true);
+// else if (sortFilesBy == 0x16)
+// m2.setItemChecked(stype, true);
+ else if (sortFilesBy == (int)QDir::Time)
+ m2.setItemChecked(sdate, true);
+ else if (sortFilesBy == (int)QDir::Unsorted)
+ m2.setItemChecked(sunsorted, true);
+
+ m.insertItem(tr("Sort"), &m2);
+
+ m.insertSeparator();
+
+ int hidden = m.insertItem(tr("Show &hidden files"));
+ m.setItemChecked(hidden, bShowHiddenFiles);
+
+ m.move(p);
+ int res = m.exec(QCursor::pos(), -1);
+
+ if (res == reload)
+ action = PA_Reload;
+ else if (res == hidden)
+ action = PA_Hidden;
+ else if (res == sname)
+ action = PA_SortName;
+// else if (res == stype)
+// action = PA_SortType;
+ else if (res == sdate)
+ action = PA_SortDate;
+ else if (res == ssize)
+ action = PA_SortSize;
+ else if (res == sunsorted)
+ action = PA_SortUnsorted;
+ }
+
+}
+
+void Q3FileDialog::deleteFile(const QString &filename)
+{
+ if (filename.isEmpty())
+ return;
+
+ QString encoded = Q3FileDialogPrivate::encodeFileName(filename);
+ QUrlInfo fi(d->url.info(encoded.isEmpty() ? QString::fromLatin1(".") : encoded));
+ QString t = tr("the file");
+ if (fi.isDir())
+ t = tr("the directory");
+ if (fi.isSymLink())
+ t = tr("the symlink");
+
+ if (QMessageBox::warning(this,
+ tr("Delete %1").arg(t),
+ tr("<qt>Are you sure you wish to delete %1 \"%2\"?</qt>")
+ .arg(t).arg(filename),
+ tr("&Yes"), tr("&No"), QString(), 1) == 0)
+ d->url.remove(Q3FileDialogPrivate::encodeFileName(filename));
+
+}
+
+void Q3FileDialog::fileSelected(int )
+{
+ // unused
+}
+
+void Q3FileDialog::fileHighlighted(int)
+{
+ // unused
+}
+
+void Q3FileDialog::dirSelected(int)
+{
+ // unused
+}
+
+void Q3FileDialog::pathSelected(int)
+{
+ // unused
+}
+
+
+void Q3FileDialog::cdUpClicked()
+{
+ QString oldName = nameEdit->text();
+ setUrl(Q3UrlOperator(d->url, QLatin1String("..")));
+ if (!oldName.isEmpty())
+ nameEdit->setText(oldName);
+}
+
+void Q3FileDialog::newFolderClicked()
+{
+ QString foldername(tr("New Folder 1"));
+ int i = 0;
+ QStringList lst;
+ Q3ListViewItemIterator it(files);
+ for (; it.current(); ++it)
+ if (it.current()->text(0).contains(tr("New Folder")))
+ lst.append(it.current()->text(0));
+
+ if (!lst.count() == 0)
+ while (lst.contains(foldername))
+ foldername = tr("New Folder %1").arg(++i);
+
+ d->url.mkdir(foldername);
+}
+
+void Q3FileDialog::createdDirectory(const QUrlInfo &info, Q3NetworkOperation *)
+{
+ resortDir();
+ if (d->moreFiles->isVisible()) {
+ for (uint i = 0; i < d->moreFiles->count(); ++i) {
+ if (d->moreFiles->text(i) == info.name()) {
+ d->moreFiles->setCurrentItem(i);
+ d->moreFiles->startRename(false);
+ break;
+ }
+ }
+ } else {
+ Q3ListViewItem *item = files->firstChild();
+ while (item) {
+ if (item->text(0) == info.name()) {
+ files->setSelected(item, true);
+ files->setCurrentItem(item);
+ files->startRename(false);
+ break;
+ }
+ item = item->nextSibling();
+ }
+ }
+}
+
+
+/*!
+ This is a convenience static function that will return an existing directory
+ selected by the user.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 12
+
+ This function creates a modal file dialog called \a name, with
+ parent, \a parent. If parent is not 0, the dialog will be shown
+ centered over the parent.
+
+ The dialog's working directory is set to \a dir, and the caption is
+ set to \a caption. Either of these may be an empty string in which case
+ the current directory and a default caption will be used respectively.
+
+ If \a dirOnly is true, then only directories will be shown in
+ the file dialog; otherwise both directories and files will be shown.
+
+ Under Unix/X11, the normal behavior of the file dialog is to resolve
+ and follow symlinks. For example, if /usr/tmp is a symlink to /var/tmp,
+ the file dialog will change to /var/tmp after entering /usr/tmp.
+ If \a resolveSymlinks is false, the file dialog will treat
+ symlinks as regular directories.
+
+ Note that on Windows the dialog will spin a blocking modal event loop
+ that will not dispatch any QTimers and if parent is not 0 then it will
+ position the dialog just under the parent's title bar.
+
+ \sa getOpenFileName(), getOpenFileNames(), getSaveFileName()
+*/
+
+QString Q3FileDialog::getExistingDirectory(const QString & dir,
+ QWidget *parent,
+ const char* name,
+ const QString& caption,
+ bool dirOnly,
+ bool resolveSymlinks)
+{
+ bool save_qt_resolve_symlinks = qt_resolve_symlinks;
+ qt_resolve_symlinks = resolveSymlinks;
+
+ makeVariables();
+ QString wd;
+ if (workingDirectory)
+ wd = *workingDirectory;
+
+#if defined(Q_WS_WIN)
+ QString initialDir;
+ if (!dir.isEmpty()) {
+ Q3UrlOperator u(dir);
+ if (QFileInfo(u.path()).isDir())
+ initialDir = dir;
+ } else
+ initialDir.clear();
+ if (qt_use_native_dialogs && qobject_cast<QWindowsStyle *>(qApp->style()) && dirOnly)
+ return winGetExistingDirectory(initialDir, parent, name, caption);
+#endif
+#if defined(Q_WS_MAC)
+ if(qt_use_native_dialogs && qobject_cast<QMacStyle *>(qApp->style()))
+ return macGetOpenFileNames(QLatin1String(""), 0, parent, name, caption,
+ 0, false, true).first().normalized(QString::NormalizationForm_C);
+#endif
+
+ Q3FileDialog *dlg = new Q3FileDialog(parent, name ? name : "qt_filedlg_ged", true);
+
+ if (!caption.isNull())
+ dlg->setWindowTitle(caption);
+ else
+ dlg->setWindowTitle(Q3FileDialog::tr("Find Directory"));
+
+ dlg->setMode(dirOnly ? DirectoryOnly : Directory);
+
+ dlg->d->types->clear();
+ dlg->d->types->insertItem(Q3FileDialog::tr("Directories"));
+ dlg->d->types->setEnabled(false);
+
+ QString dir_(dir);
+ dir_ = dir_.simplified();
+ if (dir_.isEmpty() && !wd.isEmpty())
+ dir_ = wd;
+ Q3UrlOperator u(dir_);
+ if (u.isLocalFile()) {
+ if (!dir_.isEmpty()) {
+ QFileInfo f(u.path());
+ if (f.exists())
+ if (f.isDir()) {
+ dlg->setDir(dir_);
+ wd = dir_;
+ }
+ } else if (!wd.isEmpty()) {
+ Q3Url tempUrl(wd);
+ QFileInfo f(tempUrl.path());
+ if (f.isDir()) {
+ dlg->setDir(wd);
+ }
+ } else {
+ QString theDir = dir_;
+ if (theDir.isEmpty()) {
+ theDir = toRootIfNotExists( QDir::currentDirPath() );
+ } if (!theDir.isEmpty()) {
+ Q3Url tempUrl(theDir);
+ QFileInfo f(tempUrl.path());
+ if (f.isDir()) {
+ wd = theDir;
+ dlg->setDir(theDir);
+ }
+ }
+ }
+ } else {
+ dlg->setUrl(dir_);
+ }
+
+ QString result;
+ dlg->setSelection(dlg->d->url.toString());
+
+ if (dlg->exec() == QDialog::Accepted) {
+ result = dlg->selectedFile();
+ wd = result;
+ }
+ delete dlg;
+
+ if (!result.isEmpty() && result.right(1) != QString(QLatin1Char('/')))
+ result += QLatin1Char('/');
+
+ qt_resolve_symlinks = save_qt_resolve_symlinks;
+
+ return result;
+}
+
+
+/*!
+ \property Q3FileDialog::mode
+ \brief the file dialog's mode
+
+ The default mode is \l ExistingFile.
+*/
+
+void Q3FileDialog::setMode(Mode newMode)
+{
+ if (d->mode != newMode) {
+ d->mode = newMode;
+ QString sel = d->currentFileName;
+ int maxnamelen = 255; // _POSIX_MAX_PATH
+ if (isDirectoryMode(newMode)) {
+ files->setSelectionMode(Q3ListView::Single);
+ d->moreFiles->setSelectionMode(Q3ListBox::Single);
+ if (sel.isNull())
+ sel = QString::fromLatin1(".");
+ d->types->setEnabled(false);
+ } else if (newMode == ExistingFiles) {
+ maxnamelen = INT_MAX;
+ files->setSelectionMode(Q3ListView::Extended);
+ d->moreFiles->setSelectionMode(Q3ListBox::Extended);
+ d->types->setEnabled(true);
+ } else {
+ files->setSelectionMode(Q3ListView::Single);
+ d->moreFiles->setSelectionMode(Q3ListBox::Single);
+ d->types->setEnabled(true);
+ }
+ nameEdit->setMaxLength(maxnamelen);
+ rereadDir();
+ QUrlInfo f(d->url.info(QString(QLatin1Char('.'))));
+ trySetSelection(f.isDir(), d->url, false);
+ }
+
+ QString okt;
+ bool changeFilters = false;
+ if (mode() == AnyFile) {
+ okt = tr("&Save");
+ d->fileL->setText(tr("File &name:"));
+ if (d->types->count() == 1) {
+ d->types->setCurrentItem(0);
+ if (d->types->currentText() == QLatin1String("Directories")) {
+ changeFilters = true;
+ }
+ }
+ }
+ else if (mode() == Directory || mode() == DirectoryOnly) {
+ okt = tr("&OK");
+ d->fileL->setText(tr("Directory:"));
+ d->types->clear();
+ d->types->insertItem(tr("Directories"));
+ }
+ else {
+ okt = tr("&Open");
+ d->fileL->setText(tr("File &name:"));
+ if (d->types->count() == 1) {
+ d->types->setCurrentItem(0);
+ if (d->types->currentText() == QLatin1String("Directories")) {
+ changeFilters = true;
+ }
+ }
+ }
+
+ if (changeFilters) {
+ d->types->clear();
+ d->types->insertItem(tr("All Files (*)"));
+ }
+
+ okB->setText(okt);
+}
+
+Q3FileDialog::Mode Q3FileDialog::mode() const
+{
+ return d->mode;
+}
+
+/*! \reimp
+*/
+
+void Q3FileDialog::done(int i)
+{
+ if (i == QDialog::Accepted && (d->mode == ExistingFile || d->mode == ExistingFiles)) {
+ QStringList selection = selectedFiles();
+ for (int f = 0; f < selection.count(); f++) {
+ QString file = selection[f];
+ if (file.isNull())
+ continue;
+ if (d->url.isLocalFile() && !QFile::exists(file)) {
+ QMessageBox::information(this, tr("Error"),
+ tr("%1\nFile not found.\nCheck path and filename.").arg(file));
+ return;
+ }
+ }
+ }
+ QDialog::done(i);
+}
+
+/*!
+ \property Q3FileDialog::viewMode
+
+ \brief the file dialog's view mode
+
+ If you set the view mode to be \e Detail (the default), then you
+ will see the file's details, such as the size of the file and the
+ date the file was last modified in addition to the file's name.
+
+ If you set the view mode to be \e List, then you will just
+ see a list of the files and folders.
+
+ See \l Q3FileDialog::ViewMode
+*/
+
+
+Q3FileDialog::ViewMode Q3FileDialog::viewMode() const
+{
+ if (detailViewMode)
+ return Detail;
+ else
+ return List;
+}
+
+void Q3FileDialog::setViewMode(ViewMode m)
+{
+ if (m == Detail) {
+ detailViewMode = true;
+ d->stack->raiseWidget(files);
+ d->detailView->setOn(true);
+ d->mcView->setOn(false);
+ } else if (m == List) {
+ detailViewMode = false;
+ d->stack->raiseWidget(d->moreFiles);
+ d->detailView->setOn(false);
+ d->mcView->setOn(true);
+ }
+}
+
+
+/*!
+ \property Q3FileDialog::previewMode
+
+ \brief the preview mode for the file dialog
+
+ If you set the mode to be a mode other than \e NoPreview, you must
+ use setInfoPreview() or setContentsPreview() to set the dialog's
+ preview widget to your preview widget and enable the preview
+ widget(s) with setInfoPreviewEnabled() or
+ setContentsPreviewEnabled().
+
+ \sa infoPreview, contentsPreview, viewMode
+*/
+
+void Q3FileDialog::setPreviewMode(PreviewMode m)
+{
+ if (m == NoPreview) {
+ d->previewInfo->setOn(false);
+ d->previewContents->setOn(false);
+ } else if (m == Info && d->infoPreview) {
+ d->previewInfo->setOn(true);
+ d->previewContents->setOn(false);
+ changeMode(d->modeButtons->id(d->previewInfo));
+ } else if (m == Contents && d->contentsPreview) {
+ d->previewInfo->setOn(false);
+ d->previewContents->setOn(true);
+ changeMode(d->modeButtons->id(d->previewContents));
+ }
+}
+Q3FileDialog::PreviewMode Q3FileDialog::previewMode() const
+{
+ if (d->infoPreview && d->infoPreviewWidget->isVisibleTo(const_cast<Q3FileDialog *>(this)))
+ return Info;
+ else if (d->contentsPreview
+ && d->contentsPreviewWidget->isVisibleTo(const_cast<Q3FileDialog *>(this)))
+ return Contents;
+ return NoPreview;
+}
+
+
+/*!
+ Adds the specified widgets to the bottom of the file dialog. The
+ label \a l is placed underneath the "file name" and the "file types"
+ labels. The widget \a w is placed underneath the file types combobox.
+ The button \a b is placed underneath the Cancel push button.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 13
+
+ If you don't want to have one of the widgets added, pass 0 in that
+ widget's position.
+
+ Every time you call this function, a new row of widgets will be added
+ to the bottom of the file dialog.
+
+ \sa addToolButton(), addLeftWidget(), addRightWidget()
+*/
+
+void Q3FileDialog::addWidgets(QLabel * l, QWidget * w, QPushButton * b)
+{
+ if (!l && !w && !b)
+ return;
+
+ d->geometryDirty = true;
+
+ QHBoxLayout *lay = new QHBoxLayout();
+ d->extraWidgetsLayouts.append(lay);
+ d->topLevelLayout->addLayout(lay);
+
+ if (!l)
+ l = new QLabel(this, "qt_intern_lbl");
+ d->extraLabels.append(l);
+ lay->addWidget(l);
+
+ if (!w)
+ w = new QWidget(this, "qt_intern_widget");
+ d->extraWidgets.append(w);
+ lay->addWidget(w);
+ lay->addSpacing(15);
+
+ if (b) {
+ d->extraButtons.append(b);
+ lay->addWidget(b);
+ } else {
+ QWidget *wid = new QWidget(this, "qt_extrabuttons_widget");
+ d->extraButtons.append(wid);
+ lay->addWidget(wid);
+ }
+
+ updateGeometries();
+}
+
+/*!
+ Adds the tool button \a b to the row of tool buttons at the top of the
+ file dialog. The button is appended to the right of
+ this row. If \a separator is true, a small space is inserted between the
+ last button of the row and the new button \a b.
+
+ \sa addWidgets(), addLeftWidget(), addRightWidget()
+*/
+
+void Q3FileDialog::addToolButton(QAbstractButton *b, bool separator)
+{
+ if (!b || !d->buttonLayout)
+ return;
+
+ d->geometryDirty = true;
+
+ d->toolButtons.append(b);
+ if (separator)
+ d->buttonLayout->addSpacing(8);
+ d->buttonLayout->addWidget(b);
+
+ updateGeometries();
+}
+
+/*!
+ Adds the widget \a w to the left-hand side of the file dialog.
+
+ \sa addRightWidget(), addWidgets(), addToolButton()
+*/
+
+void Q3FileDialog::addLeftWidget(QWidget *w)
+{
+ if (!w)
+ return;
+ d->geometryDirty = true;
+
+ d->leftLayout->addWidget(w);
+ d->leftLayout->addSpacing(5);
+
+ updateGeometries();
+}
+
+/*!
+ Adds the widget \a w to the right-hand side of the file dialog.
+
+ \sa addLeftWidget(), addWidgets(), addToolButton()
+*/
+
+void Q3FileDialog::addRightWidget(QWidget *w)
+{
+ if (!w)
+ return;
+ d->geometryDirty = true;
+
+ d->rightLayout->addSpacing(5);
+ d->rightLayout->addWidget(w);
+
+ updateGeometries();
+}
+
+/*! \reimp */
+
+void Q3FileDialog::keyPressEvent(QKeyEvent * ke)
+{
+ if (!d->ignoreNextKeyPress &&
+ ke && (ke->key() == Qt::Key_Enter ||
+ ke->key() == Qt::Key_Return)) {
+ ke->ignore();
+ if (d->paths->hasFocus()) {
+ ke->accept();
+ if (d->url == Q3Url(d->paths->currentText()))
+ nameEdit->setFocus();
+ } else if (d->types->hasFocus()) {
+ ke->accept();
+ // ### is there a suitable condition for this? only valid
+ // wildcards?
+ nameEdit->setFocus();
+ } else if (nameEdit->hasFocus()) {
+ if (d->currentFileName.isNull()) {
+ // maybe change directory
+ QUrlInfo i(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") :nameEdit->text()));
+ if (i.isDir()) {
+ nameEdit->setText(QString::fromLatin1(""));
+ setDir(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(i.name())));
+ }
+ ke->accept();
+ } else if (mode() == ExistingFiles) {
+ QUrlInfo i(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text()));
+ if (i.isFile()) {
+ Q3ListViewItem * i = files->firstChild();
+ while (i && nameEdit->text() != i->text(0))
+ i = i->nextSibling();
+ if (i)
+ files->setSelected(i, true);
+ else
+ ke->accept(); // strangely, means to ignore that event
+ }
+ }
+ } else if (files->hasFocus() || d->moreFiles->hasFocus()) {
+ ke->accept();
+ }
+ } else if (ke->key() == Qt::Key_Escape) {
+ ke->ignore();
+ }
+
+ d->ignoreNextKeyPress = false;
+
+ if (!ke->isAccepted()) {
+ QDialog::keyPressEvent(ke);
+ }
+}
+
+
+/*! \class Q3FileIconProvider
+
+ \brief The Q3FileIconProvider class provides icons for Q3FileDialog to
+ use.
+
+ \compat
+
+ By default Q3FileIconProvider is not used, but any application or
+ library can subclass it, reimplement pixmap() to return a suitable
+ icon, and make all Q3FileDialog objects use it by calling the static
+ function Q3FileDialog::setIconProvider().
+
+ It is advisable to make all the icons that Q3FileIconProvider returns be
+ the same size or at least the same width. This makes the list view
+ look much better.
+
+ \sa Q3FileDialog
+*/
+
+
+/*! Constructs an empty file icon provider called \a name, with the
+ parent \a parent.
+*/
+
+Q3FileIconProvider::Q3FileIconProvider(QObject * parent, const char* name)
+ : QObject(parent, name)
+{
+ // nothing necessary
+}
+
+
+/*!
+ Returns a pointer to a pixmap that should be used to
+ signify the file with the information \a info.
+
+ If pixmap() returns 0, Q3FileDialog draws the default pixmap.
+
+ The default implementation returns particular icons for files, directories,
+ link-files and link-directories. It returns a blank "icon" for other types.
+
+ If you return a pixmap here, it should measure 16x16 pixels.
+*/
+
+const QPixmap * Q3FileIconProvider::pixmap(const QFileInfo & info)
+{
+ if (info.isSymLink()) {
+ if (info.isFile())
+ return symLinkFileIcon;
+ else
+ return symLinkDirIcon;
+ } else if (info.isDir()) {
+ return closedFolderIcon;
+ } else if (info.isFile()) {
+ return fileIcon;
+ } else {
+ return fifteenTransparentPixels;
+ }
+}
+
+/*!
+ Sets the Q3FileIconProvider used by the file dialog to \a provider.
+
+ The default is that there is no Q3FileIconProvider and Q3FileDialog
+ just draws a folder icon next to each directory and nothing next
+ to files.
+
+ \sa Q3FileIconProvider, iconProvider()
+*/
+
+void Q3FileDialog::setIconProvider(Q3FileIconProvider * provider)
+{
+ fileIconProvider = provider;
+}
+
+
+/*!
+ Returns a pointer to the icon provider currently set on the file dialog.
+ By default there is no icon provider, and this function returns 0.
+
+ \sa setIconProvider(), Q3FileIconProvider
+*/
+
+Q3FileIconProvider * Q3FileDialog::iconProvider()
+{
+ return fileIconProvider;
+}
+
+
+#if defined(Q_WS_WIN)
+
+// ### FIXME: this code is duplicated in qdns.cpp
+static QString getWindowsRegString(HKEY key, const QString &subKey)
+{
+ QString s;
+
+ wchar_t buf[1024];
+ DWORD bsz = sizeof(buf) / sizeof(wchar_t);
+ int r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)buf, &bsz);
+ if (r == ERROR_SUCCESS) {
+ s = QString::fromWCharArray(buf);
+ } else if (r == ERROR_MORE_DATA) {
+ char *ptr = new char[bsz+1];
+ r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)ptr, &bsz);
+ if (r == ERROR_SUCCESS)
+ s = QLatin1String(ptr);
+ delete [] ptr;
+ }
+
+ return s;
+}
+
+QPixmap fromHICON(HICON hIcon)
+{
+ ICONINFO icoInfo;
+ if (GetIconInfo(hIcon, &icoInfo) && icoInfo.hbmColor) {
+ return QPixmap::fromWinHBITMAP(icoInfo.hbmColor);
+ }
+ return QPixmap();
+}
+
+QWindowsIconProvider::QWindowsIconProvider(QObject *parent, const char *name)
+ : Q3FileIconProvider(parent, name)
+{
+ pixw = GetSystemMetrics(SM_CXSMICON);
+ pixh = GetSystemMetrics(SM_CYSMICON);
+
+ HKEY k;
+ HICON si;
+ QString s;
+ UINT res = 0;
+
+ // ---------- get default folder pixmap
+ const wchar_t iconFolder[] = L"folder\\DefaultIcon"; // workaround for Borland
+ int r = RegOpenKeyEx(HKEY_CLASSES_ROOT, iconFolder, 0, KEY_READ, &k);
+
+ if (r == ERROR_SUCCESS) {
+ s = getWindowsRegString(k, QString());
+ RegCloseKey(k);
+
+ QStringList lst = QStringList::split(QLatin1String(","), s);
+
+ if (lst.count() >= 2) { // don't just assume that lst has two entries
+ res = ExtractIconEx((wchar_t*)lst[0].simplifyWhiteSpace().utf16(), lst[1].simplifyWhiteSpace().toInt(), 0, &si, 1);
+ }
+
+ if (res) {
+ defaultFolder = fromHICON(si);
+ defaultFolder.setMask(defaultFolder.createHeuristicMask());
+ *closedFolderIcon = defaultFolder;
+ DestroyIcon(si);
+ } else {
+ defaultFolder = *closedFolderIcon;
+ }
+ } else {
+ RegCloseKey(k);
+ }
+
+ //------------------------------- get default file pixmap
+ res = ExtractIconEx(L"shell32.dll", 0, 0, &si, 1);
+
+ if (res) {
+ defaultFile = fromHICON(si);
+ defaultFile.setMask(defaultFile.createHeuristicMask());
+ *fileIcon = defaultFile;
+ DestroyIcon(si);
+ } else {
+ defaultFile = *fileIcon;
+ }
+
+ //------------------------------- get default exe pixmap
+#ifndef Q_OS_WINCE
+ res = ExtractIconEx(L"shell32.dll", 2, 0, &si, 1);
+#else
+ res = ExtractIconEx(L"ceshell.dll", 10, 0, &si, 1);
+#endif
+
+ if (res) {
+ defaultExe = fromHICON(si);
+ defaultExe.setMask(defaultExe.createHeuristicMask());
+ DestroyIcon(si);
+ } else {
+ defaultExe = *fileIcon;
+ }
+}
+
+QWindowsIconProvider::~QWindowsIconProvider()
+{
+ if (this == fileIconProvider)
+ fileIconProvider = 0;
+}
+
+const QPixmap * QWindowsIconProvider::pixmap(const QFileInfo &fi)
+{
+ if (fi.isSymLink()) {
+ QString real = fi.symLinkTarget();
+ if (!real.isEmpty())
+ return pixmap(QFileInfo(real));
+ }
+
+ QString ext = fi.extension(false).upper();
+ QString key = ext;
+ ext.prepend(QLatin1String("."));
+ QMap< QString, QPixmap >::Iterator it;
+
+ if (fi.isDir()) {
+ return &defaultFolder;
+ } else if (ext.toLower() != QLatin1String(".exe")) {
+ it = cache.find(key);
+ if (it != cache.end())
+ return &(*it);
+
+ HKEY k, k2;
+ int r = RegOpenKeyEx(HKEY_CLASSES_ROOT, (wchar_t*)ext.utf16(), 0, KEY_READ, &k);
+ QString s;
+ if (r == ERROR_SUCCESS) {
+ s = getWindowsRegString(k, QString());
+ } else {
+ cache[key] = defaultFile;
+ RegCloseKey(k);
+ return &defaultFile;
+ }
+ RegCloseKey(k);
+
+ r = RegOpenKeyEx(HKEY_CLASSES_ROOT, (wchar_t*)QString(s + QLatin1String("\\DefaultIcon")).utf16(),
+ 0, KEY_READ, &k2);
+ if (r == ERROR_SUCCESS) {
+ s = getWindowsRegString(k2, QString());
+ } else {
+ cache[key] = defaultFile;
+ RegCloseKey(k2);
+ return &defaultFile;
+ }
+ RegCloseKey(k2);
+
+ if (s.isEmpty())
+ return &defaultFile;
+
+ QStringList lst = QStringList::split(QLatin1String(","), s);
+
+ HICON si;
+ UINT res = 0;
+ if (lst.count() >= 2) { // don't just assume that lst has two entries
+ QString filepath = lst[0].stripWhiteSpace();
+ if (!filepath.isEmpty()) {
+ if (filepath.find(QLatin1String("%1")) != -1) {
+ filepath = filepath.arg(fi.filePath());
+ if (ext.toLower() == QLatin1String(".dll")) {
+ pix = defaultFile;
+ return &pix;
+ }
+ }
+ if (filepath[0] == QLatin1Char('"') && filepath[(int)filepath.length()-1] == QLatin1Char('"'))
+ filepath = filepath.mid(1, filepath.length()-2);
+
+ res = ExtractIconEx((wchar_t*)filepath.utf16(), lst[1].stripWhiteSpace().toInt(), 0, &si, 1);
+ }
+ }
+ if (res) {
+ pix = fromHICON(si);
+ pix.setMask(pix.createHeuristicMask());
+ DestroyIcon(si);
+ } else {
+ pix = defaultFile;
+ }
+
+ cache[key] = pix;
+ return &pix;
+ } else {
+ HICON si;
+ UINT res = 0;
+ if (!fi.absFilePath().isEmpty()) {
+ res = ExtractIconEx((wchar_t*)fi.absFilePath().utf16(), -1, 0, 0, 1);
+ if (res)
+ res = ExtractIconEx((wchar_t*)fi.absFilePath().utf16(), res - 1, 0, &si, 1);
+ }
+
+ if (res) {
+ pix = fromHICON(si);
+ pix.setMask(pix.createHeuristicMask());
+ DestroyIcon(si);
+ } else {
+ pix = defaultExe;
+ }
+
+ return &pix;
+ }
+
+ // can't happen!
+ return 0;
+}
+#endif
+
+
+
+/*!
+ \reimp
+*/
+bool Q3FileDialog::eventFilter(QObject * o, QEvent * e)
+{
+ if (e->type() == QEvent::KeyPress && ((QKeyEvent*)e)->key() == Qt::Key_F5) {
+ rereadDir();
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (e->type() == QEvent::KeyPress && ((QKeyEvent*)e)->key() == Qt::Key_F2 &&
+ (o == files || o == files->viewport())) {
+ if (files->isVisible() && files->currentItem()) {
+ if (QUrlInfo(d->url.info(QString(QLatin1Char('.')))).isWritable() && files->currentItem()->text(0) != QLatin1String("..")) {
+ files->renameItem = files->currentItem();
+ files->startRename(true);
+ }
+ }
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (e->type() == QEvent::KeyPress && ((QKeyEvent*)e)->key() == Qt::Key_F2 &&
+ (o == d->moreFiles || o == d->moreFiles->viewport())) {
+ if (d->moreFiles->isVisible() && d->moreFiles->currentItem() != -1) {
+ if (QUrlInfo(d->url.info(QString(QLatin1Char('.')))).isWritable() &&
+ d->moreFiles->item(d->moreFiles->currentItem())->text() != QLatin1String("..")) {
+ d->moreFiles->renameItem = d->moreFiles->item(d->moreFiles->currentItem());
+ d->moreFiles->startRename(true);
+ }
+ }
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (e->type() == QEvent::KeyPress && d->moreFiles->renaming) {
+ d->moreFiles->lined->setFocus();
+ QApplication::sendEvent(d->moreFiles->lined, e);
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (e->type() == QEvent::KeyPress && files->renaming) {
+ files->lined->setFocus();
+ QApplication::sendEvent(files->lined, e);
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (e->type() == QEvent::KeyPress &&
+ ((QKeyEvent *)e)->key() == Qt::Key_Backspace &&
+ (o == files ||
+ o == d->moreFiles ||
+ o == files->viewport() ||
+ o == d->moreFiles->viewport())) {
+ cdUpClicked();
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (e->type() == QEvent::KeyPress &&
+ ((QKeyEvent *)e)->key() == Qt::Key_Delete &&
+ (o == files ||
+ o == files->viewport())) {
+ if (files->currentItem())
+ deleteFile(files->currentItem()->text(0));
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (e->type() == QEvent::KeyPress &&
+ ((QKeyEvent *)e)->key() == Qt::Key_Delete &&
+ (o == d->moreFiles ||
+ o == d->moreFiles->viewport())) {
+ int c = d->moreFiles->currentItem();
+ if (c >= 0)
+ deleteFile(d->moreFiles->item(c)->text());
+ ((QKeyEvent *)e)->accept();
+ return true;
+ } else if (o == files && e->type() == QEvent::FocusOut && files->currentItem()) {
+ } else if (o == files && e->type() == QEvent::KeyPress) {
+ QTimer::singleShot(0, this, SLOT(fixupNameEdit()));
+ } else if (o == nameEdit && e->type() == QEvent::KeyPress && d->mode != AnyFile) {
+ if ((nameEdit->cursorPosition() == (int)nameEdit->text().length() || nameEdit->hasSelectedText()) &&
+ isprint(((QKeyEvent *)e)->ascii())) {
+#if defined(Q_WS_WIN)
+ QString nt(nameEdit->text().toLower());
+#else
+ QString nt(nameEdit->text());
+#endif
+ nt.truncate(nameEdit->cursorPosition());
+ nt += QLatin1Char((char)(((QKeyEvent *)e)->ascii()));
+ Q3ListViewItem * i = files->firstChild();
+#if defined(Q_WS_WIN)
+ while(i && i->text(0).left(nt.length()).toLower() != nt)
+#else
+ while(i && i->text(0).left(nt.length()) != nt)
+#endif
+ i = i->nextSibling();
+ if (i) {
+ nt = i->text(0);
+ int cp = nameEdit->cursorPosition()+1;
+ nameEdit->validateAndSet(nt, cp, cp, nt.length());
+ return true;
+ }
+ }
+ } else if (o == nameEdit && e->type() == QEvent::FocusIn) {
+ fileNameEditDone();
+ } else if (d->moreFiles->renaming && o != d->moreFiles->lined && e->type() == QEvent::FocusIn) {
+ d->moreFiles->lined->setFocus();
+ return true;
+ } else if (files->renaming && o != files->lined && e->type() == QEvent::FocusIn) {
+ files->lined->setFocus();
+ return true;
+ } else if ((o == d->moreFiles || o == d->moreFiles->viewport()) &&
+ e->type() == QEvent::FocusIn) {
+ if ((o == d->moreFiles->viewport() && !d->moreFiles->viewport()->hasFocus())
+ || (o == d->moreFiles && !d->moreFiles->hasFocus()))
+ ((QWidget*)o)->setFocus();
+ return false;
+ }
+
+ return QDialog::eventFilter(o, e);
+}
+
+/*!
+ Sets the filters used in the file dialog to \a filters. Each group
+ of filters must be separated by \c{;;} (\e two semicolons).
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 14
+
+*/
+
+void Q3FileDialog::setFilters(const QString &filters)
+{
+ QStringList lst = makeFiltersList(filters);
+ setFilters(lst);
+}
+
+/*!
+ \overload
+
+ \a types must be a null-terminated list of strings.
+
+*/
+
+void Q3FileDialog::setFilters(const char ** types)
+{
+ if (!types || !*types)
+ return;
+
+ d->types->clear();
+ while(types && *types) {
+ d->types->insertItem(QString::fromLatin1(*types));
+ types++;
+ }
+ d->types->setCurrentItem(0);
+ setFilter(d->types->text(0));
+}
+
+
+/*!
+ \overload
+
+ \a types is a list of filter strings.
+*/
+
+void Q3FileDialog::setFilters(const QStringList & types)
+{
+ if (types.count() < 1)
+ return;
+
+ d->types->clear();
+ for (QStringList::ConstIterator it = types.begin(); it != types.end(); ++it)
+ d->types->insertItem(*it);
+ d->types->setCurrentItem(0);
+ setFilter(d->types->text(0));
+}
+
+/*!
+ Adds the filter \a filter to the list of filters and makes it the
+ current filter.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 15
+
+ In the above example, a file dialog is created, and the file filter "Images
+ (*.png *.jpg *.xpm)" is added and is set as the current filter. The original
+ filter, "All Files (*)", is still available.
+
+ \sa setFilter(), setFilters()
+*/
+
+void Q3FileDialog::addFilter(const QString &filter)
+{
+ if (filter.isEmpty())
+ return;
+ QString f = filter;
+ QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp));
+ int index = r.indexIn(f);
+ if (index >= 0)
+ f = r.cap(2);
+ for (int i = 0; i < d->types->count(); ++i) {
+ QString f2(d->types->text(i));
+ int index = r.indexIn(f2);
+ if (index >= 0)
+ f2 = r.cap(1);
+ if (f2 == f) {
+ d->types->setCurrentItem(i);
+ setFilter(f2);
+ return;
+ }
+ }
+
+ d->types->insertItem(filter);
+ d->types->setCurrentItem(d->types->count() - 1);
+ setFilter(d->types->text(d->types->count() - 1));
+}
+
+/*!
+ Since modeButtons is a top-level widget, it may be destroyed by the
+ kernel at application exit. Notice if this happens to
+ avoid double deletion.
+*/
+
+void Q3FileDialog::modeButtonsDestroyed()
+{
+ if (d)
+ d->modeButtons = 0;
+}
+
+
+/*!
+ This is a convenience static function that will return one or more
+ existing files selected by the user.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 16
+
+ This function creates a modal file dialog called \a name, with
+ parent \a parent. If \a parent is not 0, the dialog will be shown
+ centered over the parent.
+
+ The file dialog's working directory will be set to \a dir. If \a
+ dir includes a file name, the file will be selected. The filter
+ is set to \a filter so that only those files which match the filter
+ are shown. The filter selected is set to \a selectedFilter. The parameters
+ \a dir, \a selectedFilter and \a filter may be empty strings.
+
+ The dialog's caption is set to \a caption. If \a caption is not
+ specified then a default caption will be used.
+
+ Under Windows and Mac OS X, this static function will use the native
+ file dialog and not a Q3FileDialog, unless the style of the application
+ is set to something other than the native style. (Note that on Windows the
+ dialog will spin a blocking modal event loop that will not dispatch any
+ QTimers and if parent is not 0 then it will position the dialog just under
+ the parent's title bar).
+
+ Under Unix/X11, the normal behavior of the file dialog is to resolve
+ and follow symlinks. For example, if /usr/tmp is a symlink to /var/tmp,
+ the file dialog will change to /var/tmp after entering /usr/tmp.
+ If \a resolveSymlinks is false, the file dialog will treat
+ symlinks as regular directories.
+
+ Note that if you want to iterate over the list of files, you should
+ iterate over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 17
+
+ \sa getOpenFileName(), getSaveFileName(), getExistingDirectory()
+*/
+
+QStringList Q3FileDialog::getOpenFileNames(const QString & filter,
+ const QString& dir,
+ QWidget *parent,
+ const char* name,
+ const QString& caption,
+ QString *selectedFilter,
+ bool resolveSymlinks)
+{
+ bool save_qt_resolve_symlinks = qt_resolve_symlinks;
+ qt_resolve_symlinks = resolveSymlinks;
+
+ QStringList filters;
+ if (!filter.isEmpty())
+ filters = makeFiltersList(filter);
+
+ makeVariables();
+
+ if (workingDirectory->isNull())
+ *workingDirectory = toRootIfNotExists( QDir::currentDirPath() );
+
+ if (!dir.isEmpty()) {
+ // #### works only correct for local files
+ Q3UrlOperator u(Q3FileDialogPrivate::encodeFileName(dir));
+ if (u.isLocalFile() && QFileInfo(u.path()).isDir()) {
+ *workingDirectory = dir;
+ } else {
+ *workingDirectory = u.toString();
+ }
+ }
+
+#if defined(Q_WS_WIN)
+ if (qt_use_native_dialogs && qobject_cast<QWindowsStyle *>(qApp->style()))
+ return winGetOpenFileNames(filter, workingDirectory, parent, name, caption, selectedFilter);
+#elif defined(Q_WS_MAC)
+ if (qt_use_native_dialogs && qobject_cast<QMacStyle *>(qApp->style())) {
+ QStringList sl = macGetOpenFileNames(filter, dir.isEmpty() ? 0 : workingDirectory,
+ parent, name, caption, selectedFilter);
+ for (int i = 0; i < sl.count(); ++i)
+ sl.replace(i, sl.at(i).normalized(QString::NormalizationForm_C));
+ return sl;
+ }
+#endif
+
+ Q3FileDialog *dlg = new Q3FileDialog(*workingDirectory, QString(), parent, name ? name : "qt_filedlg_gofns", true);
+
+ if (!caption.isNull())
+ dlg->setWindowTitle(caption);
+ else
+ dlg->setWindowTitle(Q3FileDialog::tr("Open"));
+
+ dlg->setFilters(filters);
+ if (selectedFilter)
+ dlg->setFilter(*selectedFilter);
+ dlg->setMode(Q3FileDialog::ExistingFiles);
+ QString result;
+ QStringList lst;
+ if (dlg->exec() == QDialog::Accepted) {
+ lst = dlg->selectedFiles();
+ *workingDirectory = dlg->d->url;
+ if (selectedFilter)
+ *selectedFilter = dlg->selectedFilter();
+ }
+ delete dlg;
+
+ qt_resolve_symlinks = save_qt_resolve_symlinks;
+
+ return lst;
+}
+
+/*! Updates the line edit to match the speed-key usage in Q3ListView. */
+
+void Q3FileDialog::fixupNameEdit()
+{
+ if (files->currentItem()) {
+ if (((Q3FileDialogPrivate::File*)files->currentItem())->info.isFile())
+ nameEdit->setText(files->currentItem()->text(0));
+ }
+}
+
+/*!
+ Returns the URL of the current working directory in the file dialog.
+
+ \sa setUrl()
+*/
+
+Q3Url Q3FileDialog::url() const
+{
+ return d->url;
+}
+
+static bool isRoot(const Q3Url &u)
+{
+#if defined(Q_OS_UNIX)
+ if (u.path() == QString(QLatin1Char('/')))
+ return true;
+#elif defined(Q_OS_WIN32)
+ QString p = u.path();
+ if (p.length() == 3 &&
+ p.right(2) == QLatin1String(":/"))
+ return true;
+ if (p[0] == QLatin1Char('/') && p[1] == QLatin1Char('/')) {
+ int slashes = p.count(QLatin1Char('/'));
+ if (slashes <= 3)
+ return true;
+ if (slashes == 4 && p[(int)p.length() - 1] == QLatin1Char('/'))
+ return true;
+ }
+#else
+#if defined(Q_CC_GNU)
+#warning "case not covered.."
+#endif
+#endif
+
+ if (!u.isLocalFile() && u.path() == QString(QLatin1Char('/')))
+ return true;
+
+ return false;
+}
+
+#if defined(Q_WS_WIN)
+extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
+#endif
+
+void Q3FileDialog::urlStart(Q3NetworkOperation *op)
+{
+ if (!op)
+ return;
+
+#if defined(Q_WS_WIN)
+ old_qt_ntfs_permission_lookup = qt_ntfs_permission_lookup;
+ qt_ntfs_permission_lookup = 0;
+#endif
+ if (op->operation() == Q3NetworkProtocol::OpListChildren) {
+#ifndef QT_NO_CURSOR
+ if (!d->cursorOverride) {
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ d->cursorOverride = true;
+ }
+#endif
+ if (isRoot(d->url))
+ d->cdToParent->setEnabled(false);
+ else
+ d->cdToParent->setEnabled(true);
+ d->mimeTypeTimer->stop();
+ d->sortedList.clear();
+ d->pendingItems.clear();
+ d->moreFiles->clearSelection();
+ files->clearSelection();
+ d->moreFiles->clear();
+ files->clear();
+ files->setSorting(-1);
+
+ QString s = d->url.toString(false, false);
+ bool found = false;
+ for (int i = 0; i < d->paths->count(); ++i) {
+#if defined(Q_WS_WIN)
+ if (d->paths->text(i).toLower() == s.toLower()) {
+#else
+ if (d->paths->text(i) == s) {
+#endif
+ found = true;
+ d->paths->setCurrentItem(i);
+ break;
+ }
+ }
+ if (!found) {
+ d->paths->insertItem(*openFolderIcon, s, -1);
+ d->paths->setCurrentItem(d->paths->count() - 1);
+ }
+ d->last = 0;
+ d->hadDotDot = false;
+
+ if (d->goBack && (d->history.isEmpty() || d->history.last() != d->url.toString())) {
+ d->history.append(d->url.toString());
+ if (d->history.count() > 1)
+ d->goBack->setEnabled(true);
+ }
+ }
+}
+
+void Q3FileDialog::urlFinished(Q3NetworkOperation *op)
+{
+ if (!op)
+ return;
+
+#ifndef QT_NO_CURSOR
+ if (op->operation() == Q3NetworkProtocol::OpListChildren &&
+ d->cursorOverride) {
+ QApplication::restoreOverrideCursor();
+ d->cursorOverride = false;
+ }
+#endif
+
+ if (op->state() == Q3NetworkProtocol::StFailed) {
+ if (d->paths->hasFocus())
+ d->ignoreNextKeyPress = true;
+
+ if (d->progressDia) {
+ d->ignoreStop = true;
+ d->progressDia->close();
+ delete d->progressDia;
+ d->progressDia = 0;
+ }
+
+ int ecode = op->errorCode();
+ QMessageBox::critical(this, tr("Error"), op->protocolDetail());
+
+ if (ecode == Q3NetworkProtocol::ErrListChildren || ecode == Q3NetworkProtocol::ErrParse ||
+ ecode == Q3NetworkProtocol::ErrUnknownProtocol || ecode == Q3NetworkProtocol::ErrLoginIncorrect ||
+ ecode == Q3NetworkProtocol::ErrValid || ecode == Q3NetworkProtocol::ErrHostNotFound ||
+ ecode == Q3NetworkProtocol::ErrFileNotExisting) {
+ d->url = d->oldUrl;
+ rereadDir();
+ } else {
+ // another error happened, no need to go back to last dir
+ }
+ } else if (op->operation() == Q3NetworkProtocol::OpListChildren &&
+ op == d->currListChildren) {
+ if (!d->hadDotDot && !isRoot(d->url)) {
+ bool ok = true;
+#if defined(Q_WS_WIN)
+ if (d->url.path().left(2) == QLatin1String("//"))
+ ok = false;
+#endif
+ if (ok) {
+ QUrlInfo ui(d->url.info(QLatin1String("..")));
+ ui.setName(QLatin1String(".."));
+ ui.setDir(true);
+ ui.setFile(false);
+ ui.setSymLink(false);
+ ui.setSize(0);
+ Q3ValueList<QUrlInfo> lst;
+ lst << ui;
+ insertEntry(lst, 0);
+ }
+ }
+ resortDir();
+ } else if (op->operation() == Q3NetworkProtocol::OpGet) {
+ } else if (op->operation() == Q3NetworkProtocol::OpPut) {
+ rereadDir();
+ if (d->progressDia) {
+ d->ignoreStop = true;
+ d->progressDia->close();
+ }
+ delete d->progressDia;
+ d->progressDia = 0;
+ }
+
+#if defined(Q_WS_WIN)
+ qt_ntfs_permission_lookup = old_qt_ntfs_permission_lookup;
+#endif
+}
+
+void Q3FileDialog::dataTransferProgress(int bytesDone, int bytesTotal, Q3NetworkOperation *op)
+{
+ if (!op)
+ return;
+
+ QString label;
+ Q3Url u(op->arg(0));
+ if (u.isLocalFile()) {
+ label = u.path();
+ } else {
+ label = QLatin1String("%1 (on %2)");
+ label = label.arg(u.path()).arg(u.host());
+ }
+
+ if (!d->progressDia) {
+ if (bytesDone < bytesTotal) {
+ d->ignoreStop = false;
+ d->progressDia = new QFDProgressDialog(this, label, bytesTotal);
+ connect(d->progressDia, SIGNAL(cancelled()),
+ this, SLOT(stopCopy()));
+ d->progressDia->show();
+ } else
+ return;
+ }
+
+ if (d->progressDia) {
+ if (op->operation() == Q3NetworkProtocol::OpGet) {
+ if (d->progressDia) {
+ d->progressDia->setReadProgress(bytesDone);
+ }
+ } else if (op->operation() == Q3NetworkProtocol::OpPut) {
+ if (d->progressDia) {
+ d->progressDia->setWriteLabel(label);
+ d->progressDia->setWriteProgress(bytesDone);
+ }
+ } else {
+ return;
+ }
+ }
+}
+
+void Q3FileDialog::insertEntry(const Q3ValueList<QUrlInfo> &lst, Q3NetworkOperation *op)
+{
+ if (op && op->operation() == Q3NetworkProtocol::OpListChildren &&
+ op != d->currListChildren)
+ return;
+ Q3ValueList<QUrlInfo>::ConstIterator it = lst.begin();
+ for (; it != lst.end(); ++it) {
+ const QUrlInfo &inf = *it;
+ if (d->mode == DirectoryOnly && !inf.isDir())
+ continue;
+ if (inf.name() == QLatin1String("..")) {
+ d->hadDotDot = true;
+ if (isRoot(d->url))
+ continue;
+#if defined(Q_WS_WIN)
+ if (d->url.path().left(2) == QLatin1String("//"))
+ continue;
+#endif
+ } else if (inf.name() == QString(QLatin1Char('.')))
+ continue;
+
+#if defined(Q_WS_WIN)
+ // Workaround a Windows bug, '..' is apparantly hidden in directories
+ // that are one level away from root
+ if (!bShowHiddenFiles && inf.name() != QLatin1String("..")) {
+ if (d->url.isLocalFile()) {
+ QString file = d->url.path();
+ if (!file.endsWith(QLatin1Char('/')))
+ file.append(QLatin1Char('/'));
+ file += inf.name();
+ if (GetFileAttributes((wchar_t*)file.utf16()) & FILE_ATTRIBUTE_HIDDEN)
+ continue;
+ } else {
+ if (inf.name() != QLatin1String("..") && inf.name()[0] == QLatin1Char('.'))
+ continue;
+ }
+ }
+#else
+ if (!bShowHiddenFiles && inf.name() != QLatin1String("..")) {
+ if (inf.name()[0] == QLatin1Char('.'))
+ continue;
+ }
+#endif
+ if (!d->url.isLocalFile()) {
+ Q3FileDialogPrivate::File * i = 0;
+ Q3FileDialogPrivate::MCItem *i2 = 0;
+ i = new Q3FileDialogPrivate::File(d, &inf, files);
+ i2 = new Q3FileDialogPrivate::MCItem(d->moreFiles, i);
+
+ if ((d->mode == ExistingFiles && inf.isDir())
+ || (isDirectoryMode(d->mode) && inf.isFile())) {
+ i->setSelectable(false);
+ i2->setSelectable(false);
+ }
+
+ i->i = i2;
+ }
+
+ d->sortedList.append(new QUrlInfo(inf));
+ }
+}
+
+void Q3FileDialog::removeEntry(Q3NetworkOperation *op)
+{
+ if (!op)
+ return;
+
+ QUrlInfo *i = 0;
+ Q3ListViewItemIterator it(files);
+ bool ok1 = false, ok2 = false;
+ for (i = d->sortedList.first(); it.current(); ++it, i = d->sortedList.next()) {
+ QString encName = Q3FileDialogPrivate::encodeFileName(
+ ((Q3FileDialogPrivate::File*)it.current())->info.name());
+ if (encName == op->arg(0)) {
+ d->pendingItems.removeRef((Q3FileDialogPrivate::File*)it.current());
+ delete ((Q3FileDialogPrivate::File*)it.current())->i;
+ delete it.current();
+ ok1 = true;
+ }
+ if (i && i->name() == op->arg(0)) {
+ d->sortedList.removeRef(i);
+ i = d->sortedList.prev();
+ ok2 = true;
+ }
+ if (ok1 && ok2)
+ break;
+ }
+}
+
+void Q3FileDialog::itemChanged(Q3NetworkOperation *op)
+{
+ if (!op)
+ return;
+
+ QUrlInfo *i = 0;
+ Q3ListViewItemIterator it1(files);
+ bool ok1 = false, ok2 = false;
+ // first check whether the new file replaces an existing file.
+ for (i = d->sortedList.first(); it1.current(); ++it1, i = d->sortedList.next()) {
+ if (((Q3FileDialogPrivate::File*)it1.current())->info.name() == op->arg(1)) {
+ delete ((Q3FileDialogPrivate::File*)it1.current())->i;
+ delete it1.current();
+ ok1 = true;
+ }
+ if (i && i->name() == op->arg(1)) {
+ d->sortedList.removeRef(i);
+ i = d->sortedList.prev();
+ ok2 = true;
+ }
+ if (ok1 && ok2)
+ break;
+ }
+
+ i = 0;
+ Q3ListViewItemIterator it(files);
+ ok1 = false;
+ ok2 = false;
+ for (i = d->sortedList.first(); it.current(); ++it, i = d->sortedList.next()) {
+ if (((Q3FileDialogPrivate::File*)it.current())->info.name() == op->arg(0)) {
+ ((Q3FileDialogPrivate::File*)it.current())->info.setName(op->arg(1));
+ ok1 = true;
+ }
+ if (i && i->name() == op->arg(0)) {
+ i->setName(op->arg(1));
+ ok2 = true;
+ }
+ if (ok1 && ok2)
+ break;
+ }
+
+ resortDir();
+}
+
+/*!
+ \property Q3FileDialog::infoPreview
+
+ \brief whether the file dialog can provide preview information about
+ the currently selected file
+
+ The default is false.
+*/
+bool Q3FileDialog::isInfoPreviewEnabled() const
+{
+ return d->infoPreview;
+}
+
+void Q3FileDialog::setInfoPreviewEnabled(bool info)
+{
+ if (info == d->infoPreview)
+ return;
+ d->geometryDirty = true;
+ d->infoPreview = info;
+ updateGeometries();
+}
+
+
+/*!
+ \property Q3FileDialog::contentsPreview
+
+ \brief whether the file dialog can provide a contents preview of the
+ currently selected file
+
+ The default is false.
+
+ \sa setContentsPreview() setInfoPreviewEnabled()
+*/
+// ### improve the above documentation: how is the preview done, how can I add
+// support for customized preview, etc.
+
+bool Q3FileDialog::isContentsPreviewEnabled() const
+{
+ return d->contentsPreview;
+}
+
+void Q3FileDialog::setContentsPreviewEnabled(bool contents)
+{
+ if (contents == d->contentsPreview)
+ return;
+ d->geometryDirty = true;
+ d->contentsPreview = contents;
+ updateGeometries();
+}
+
+
+/*!
+ Sets the widget to be used for displaying information about the file
+ to the widget \a w and a preview of that information to the
+ Q3FilePreview \a preview.
+
+ Normally you would create a preview widget that derives from both QWidget and
+ Q3FilePreview, so you should pass the same widget twice.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 18
+
+ \sa setContentsPreview(), setInfoPreviewEnabled(), setPreviewMode()
+
+*/
+
+void Q3FileDialog::setInfoPreview(QWidget *w, Q3FilePreview *preview)
+{
+ if (!w || !preview)
+ return;
+
+ if (d->infoPreviewWidget) {
+ d->preview->removeWidget(d->infoPreviewWidget);
+ if ((void*)d->infoPreviewer == (void*)d->infoPreviewWidget)
+ d->infoPreviewer = 0;
+ delete d->infoPreviewWidget;
+ }
+ if (d->infoPreviewer)
+ delete d->infoPreviewer;
+ d->infoPreviewWidget = w;
+ d->infoPreviewer = preview;
+ w->reparent(d->preview, 0, QPoint(0, 0));
+}
+
+/*!
+ Sets the widget to be used for displaying the contents of the file
+ to the widget \a w and a preview of those contents to the
+ Q3FilePreview \a preview.
+
+ Normally you would create a preview widget that derives from both QWidget and
+ Q3FilePreview, so you should pass the same widget twice.
+
+ \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 19
+
+ \sa setContentsPreviewEnabled(), setInfoPreview(), setPreviewMode()
+*/
+
+void Q3FileDialog::setContentsPreview(QWidget *w, Q3FilePreview *preview)
+{
+ if (!w || !preview)
+ return;
+
+ if (d->contentsPreviewWidget) {
+ d->preview->removeWidget(d->contentsPreviewWidget);
+ if ((void*)d->contentsPreviewWidget == (void*)d->contentsPreviewer)
+ d->contentsPreviewer = 0;
+ delete d->contentsPreviewWidget;
+ }
+ if (d->contentsPreviewer)
+ delete d->contentsPreviewer;
+ d->contentsPreviewWidget = w;
+ d->contentsPreviewer = preview;
+ w->reparent(d->preview, 0, QPoint(0, 0));
+}
+
+/*!
+ Re-sorts the displayed directory.
+
+ \sa rereadDir()
+*/
+
+void Q3FileDialog::resortDir()
+{
+ d->mimeTypeTimer->stop();
+ d->pendingItems.clear();
+
+ Q3FileDialogPrivate::File *item = 0;
+ Q3FileDialogPrivate::MCItem *item2 = 0;
+
+ d->sortedList.sort();
+
+ if (files->childCount() > 0 || d->moreFiles->count() > 0) {
+ d->moreFiles->clear();
+ files->clear();
+ d->last = 0;
+ files->setSorting(-1);
+ }
+
+ QUrlInfo *i = sortAscending ? d->sortedList.first() : d->sortedList.last();
+ for (; i; i = sortAscending ? d->sortedList.next() : d->sortedList.prev()) {
+ item = new Q3FileDialogPrivate::File(d, i, files);
+ item2 = new Q3FileDialogPrivate::MCItem(d->moreFiles, item, item2);
+ item->i = item2;
+ d->pendingItems.append(item);
+ if ((d->mode == ExistingFiles && item->info.isDir()) ||
+ (isDirectoryMode(d->mode) && item->info.isFile())) {
+ item->setSelectable(false);
+ item2->setSelectable(false);
+ }
+ }
+
+ // ##### As the Q3FileIconProvider only support QFileInfo and no
+ // QUrlInfo it can be only used for local files at the moment. In
+ // 3.0 we have to change the API of Q3FileIconProvider to work on
+ // QUrlInfo so that also remote filesystems can be show mime-type
+ // specific icons.
+ if (d->url.isLocalFile())
+ d->mimeTypeTimer->start(0);
+}
+
+/*!
+ Stops the current copy operation.
+*/
+
+void Q3FileDialog::stopCopy()
+{
+ if (d->ignoreStop)
+ return;
+
+ d->url.blockSignals(true);
+ d->url.stop();
+ if (d->progressDia) {
+ d->ignoreStop = true;
+ QTimer::singleShot(100, this, SLOT(removeProgressDia()));
+ }
+ d->url.blockSignals(false);
+}
+
+/*!
+ \internal
+*/
+
+void Q3FileDialog::removeProgressDia()
+{
+ if (d->progressDia)
+ delete d->progressDia;
+ d->progressDia = 0;
+}
+
+/*!
+ \internal
+*/
+
+void Q3FileDialog::doMimeTypeLookup()
+{
+ if (!iconProvider()) {
+ d->pendingItems.clear();
+ d->mimeTypeTimer->stop();
+ return;
+ }
+
+ d->mimeTypeTimer->stop();
+ if (d->pendingItems.count() == 0) {
+ return;
+ }
+
+ QRect r;
+ Q3FileDialogPrivate::File *item = d->pendingItems.first();
+ if (item) {
+ QFileInfo fi;
+ if (d->url.isLocalFile()) {
+ fi.setFile(Q3Url(d->url.path(), Q3FileDialogPrivate::encodeFileName(item->info.name())).path(false));
+ } else
+ fi.setFile(item->info.name()); // #####
+ const QPixmap *p = iconProvider()->pixmap(fi);
+ if (p && p != item->pixmap(0) &&
+ (!item->pixmap(0) || p->serialNumber() != item->pixmap(0)->serialNumber()) &&
+ p != fifteenTransparentPixels) {
+ item->hasMimePixmap = true;
+
+ // evil hack to avoid much too much repaints!
+ QPointer<Q3FileDialog> that(this); // this may be deleted by an event handler
+ qApp->processEvents();
+ if (that.isNull())
+ return;
+ files->setUpdatesEnabled(false);
+ files->viewport()->setUpdatesEnabled(false);
+ if (item != d->pendingItems.first())
+ return;
+ item->setPixmap(0, *p);
+ qApp->processEvents();
+ if (that.isNull())
+ return;
+ files->setUpdatesEnabled(true);
+ files->viewport()->setUpdatesEnabled(true);
+
+ if (files->isVisible()) {
+ QRect ir(files->itemRect(item));
+ if (ir != QRect(0, 0, -1, -1)) {
+ r = r.united(ir);
+ }
+ } else {
+ QRect ir(d->moreFiles->itemRect(item->i));
+ if (ir != QRect(0, 0, -1, -1)) {
+ r = r.united(ir);
+ }
+ }
+ }
+ if (d->pendingItems.count())
+ d->pendingItems.removeFirst();
+ }
+
+ if (d->moreFiles->isVisible()) {
+ d->moreFiles->viewport()->repaint(r);
+ } else {
+ files->viewport()->repaint(r);
+ }
+
+ if (d->pendingItems.count())
+ d->mimeTypeTimer->start(0);
+ else if (d->moreFiles->isVisible())
+ d->moreFiles->triggerUpdate(true);
+}
+
+/*!
+ If \a b is true then all the files in the current directory are selected;
+ otherwise, they are deselected.
+*/
+
+void Q3FileDialog::selectAll(bool b)
+{
+ if (d->mode != ExistingFiles)
+ return;
+ d->moreFiles->selectAll(b);
+ files->selectAll(b);
+}
+
+void Q3FileDialog::goBack()
+{
+ if (!d->goBack || !d->goBack->isEnabled() || d->history.isEmpty())
+ return;
+ d->history.removeLast();
+ if (d->history.size() < 2)
+ d->goBack->setEnabled(false);
+ setUrl(d->history.last());
+}
+
+// a class with wonderfully inflexible flexibility. why doesn't it
+// just subclass QWidget in the first place? 'you have to derive your
+// preview widget from QWidget and from this class' indeed.
+
+/*!
+ \class Q3FilePreview
+ \brief The Q3FilePreview class provides file previewing in Q3FileDialog.
+
+ \compat
+
+ This class is an abstract base class which is used to implement
+ widgets that can display a preview of a file in a Q3FileDialog.
+
+ You must derive the preview widget from both QWidget and from this
+ class. Then you must reimplement this class's previewUrl() function,
+ which is called by the file dialog if the preview of a file
+ (specified as a URL) should be shown.
+
+ See also Q3FileDialog::setPreviewMode(), Q3FileDialog::setContentsPreview(),
+ Q3FileDialog::setInfoPreview(), Q3FileDialog::setInfoPreviewEnabled(),
+ Q3FileDialog::setContentsPreviewEnabled().
+*/
+
+/*!
+ Constructs the Q3FilePreview.
+*/
+
+Q3FilePreview::Q3FilePreview()
+{
+}
+
+/*!
+ \fn Q3FilePreview::~Q3FilePreview()
+
+ Destroys the file preview object.
+*/
+
+/*!
+ \fn void Q3FilePreview::previewUrl(const Q3Url &url)
+
+ This function is called by Q3FileDialog if a preview
+ for the \a url should be shown. Reimplement this
+ function to provide file previewing.
+*/
+
+
+QT_END_NAMESPACE
+
+#include "moc_q3filedialog.cpp"
+#include "q3filedialog.moc"
+
+#endif
diff --git a/src/qt3support/dialogs/q3filedialog.h b/src/qt3support/dialogs/q3filedialog.h
new file mode 100644
index 0000000..66d33bd
--- /dev/null
+++ b/src/qt3support/dialogs/q3filedialog.h
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3FILEDIALOG_H
+#define Q3FILEDIALOG_H
+
+#include <QtCore/qdir.h>
+#include <QtGui/qdialog.h>
+#include <Qt3Support/q3urloperator.h>
+#include <Qt3Support/q3valuelist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QAbstractButton;
+class QPushButton;
+class QLabel;
+class QWidget;
+class QFileDialog;
+class QTimer;
+class Q3NetworkOperation;
+class QLineEdit;
+class Q3ListViewItem;
+class Q3ListBoxItem;
+class Q3FileDialogPrivate;
+class Q3FileDialogQFileListView;
+class QUrlInfo;
+
+#ifndef QT_NO_FILEDIALOG
+
+class Q_COMPAT_EXPORT Q3FileIconProvider : public QObject
+{
+ Q_OBJECT
+public:
+ Q3FileIconProvider(QObject * parent = 0, const char* name = 0);
+ virtual const QPixmap * pixmap(const QFileInfo &);
+
+private:
+ Q_DISABLE_COPY(Q3FileIconProvider)
+};
+
+class Q_COMPAT_EXPORT Q3FilePreview
+{
+public:
+ Q3FilePreview();
+ virtual ~Q3FilePreview() {}
+ virtual void previewUrl(const Q3Url &url) = 0;
+
+};
+
+class Q_COMPAT_EXPORT Q3FileDialog : public QDialog
+{
+ Q_OBJECT
+ Q_ENUMS(Mode ViewMode PreviewMode)
+ // ##### Why are this read-only properties ?
+ Q_PROPERTY(QString selectedFile READ selectedFile)
+ Q_PROPERTY(QString selectedFilter READ selectedFilter)
+ Q_PROPERTY(QStringList selectedFiles READ selectedFiles)
+ // #### Should not we be able to set the path ?
+ Q_PROPERTY(QString dirPath READ dirPath)
+ Q_PROPERTY(bool showHiddenFiles READ showHiddenFiles WRITE setShowHiddenFiles)
+ Q_PROPERTY(Mode mode READ mode WRITE setMode)
+ Q_PROPERTY(ViewMode viewMode READ viewMode WRITE setViewMode)
+ Q_PROPERTY(PreviewMode previewMode READ previewMode WRITE setPreviewMode)
+ Q_PROPERTY(bool infoPreview READ isInfoPreviewEnabled WRITE setInfoPreviewEnabled)
+ Q_PROPERTY(bool contentsPreview READ isContentsPreviewEnabled WRITE setContentsPreviewEnabled)
+
+public:
+ Q3FileDialog(const QString& dirName, const QString& filter = QString(),
+ QWidget* parent=0, const char* name=0, bool modal = false);
+ Q3FileDialog(QWidget* parent=0, const char* name=0, bool modal = false);
+ ~Q3FileDialog();
+
+ // recommended static functions
+
+ static QString getOpenFileName(const QString &initially = QString(),
+ const QString &filter = QString(),
+ QWidget *parent = 0, const char* name = 0,
+ const QString &caption = QString(),
+ QString *selectedFilter = 0,
+ bool resolveSymlinks = true);
+ static QString getSaveFileName(const QString &initially = QString(),
+ const QString &filter = QString(),
+ QWidget *parent = 0, const char* name = 0,
+ const QString &caption = QString(),
+ QString *selectedFilter = 0,
+ bool resolveSymlinks = true);
+ static QString getExistingDirectory(const QString &dir = QString(),
+ QWidget *parent = 0,
+ const char* name = 0,
+ const QString &caption = QString(),
+ bool dirOnly = true,
+ bool resolveSymlinks = true);
+ static QStringList getOpenFileNames(const QString &filter= QString(),
+ const QString &dir = QString(),
+ QWidget *parent = 0,
+ const char* name = 0,
+ const QString &caption = QString(),
+ QString *selectedFilter = 0,
+ bool resolveSymlinks = true);
+
+ // other static functions
+
+ static void setIconProvider(Q3FileIconProvider *);
+ static Q3FileIconProvider * iconProvider();
+
+ // non-static function for special needs
+
+ QString selectedFile() const;
+ QString selectedFilter() const;
+ virtual void setSelectedFilter(const QString&);
+ virtual void setSelectedFilter(int);
+
+ void setSelection(const QString &);
+
+ void selectAll(bool b);
+
+ QStringList selectedFiles() const;
+
+ QString dirPath() const;
+
+ void setDir(const QDir &);
+ const QDir *dir() const;
+
+ void setShowHiddenFiles(bool s);
+ bool showHiddenFiles() const;
+
+ void rereadDir();
+ void resortDir();
+
+ enum Mode { AnyFile, ExistingFile, Directory, ExistingFiles, DirectoryOnly };
+ void setMode(Mode);
+ Mode mode() const;
+
+ enum ViewMode { Detail, List };
+ enum PreviewMode { NoPreview, Contents, Info };
+ void setViewMode(ViewMode m);
+ ViewMode viewMode() const;
+ void setPreviewMode(PreviewMode m);
+ PreviewMode previewMode() const;
+
+ bool eventFilter(QObject *, QEvent *);
+
+ bool isInfoPreviewEnabled() const;
+ bool isContentsPreviewEnabled() const;
+ void setInfoPreviewEnabled(bool);
+ void setContentsPreviewEnabled(bool);
+
+ void setInfoPreview(QWidget *w, Q3FilePreview *preview);
+ void setContentsPreview(QWidget *w, Q3FilePreview *preview);
+
+ Q3Url url() const;
+
+ void addFilter(const QString &filter);
+
+public Q_SLOTS:
+ void done(int);
+ void setDir(const QString&);
+ void setUrl(const Q3UrlOperator &url);
+ void setFilter(const QString&);
+ void setFilters(const QString&);
+ void setFilters(const char **);
+ void setFilters(const QStringList&);
+
+protected:
+ void resizeEvent(QResizeEvent *);
+ void keyPressEvent(QKeyEvent *);
+
+ void addWidgets(QLabel *, QWidget *, QPushButton *);
+ void addToolButton(QAbstractButton *b, bool separator = false);
+ void addLeftWidget(QWidget *w);
+ void addRightWidget(QWidget *w);
+
+Q_SIGNALS:
+ void fileHighlighted(const QString&);
+ void fileSelected(const QString&);
+ void filesSelected(const QStringList&);
+ void dirEntered(const QString&);
+ void filterSelected(const QString&);
+
+private Q_SLOTS:
+ void detailViewSelectionChanged();
+ void listBoxSelectionChanged();
+ void changeMode(int);
+ void fileNameEditReturnPressed();
+ void stopCopy();
+ void removeProgressDia();
+
+ void fileSelected(int);
+ void fileHighlighted(int);
+ void dirSelected(int);
+ void pathSelected(int);
+
+ void updateFileNameEdit(Q3ListViewItem *);
+ void selectDirectoryOrFile(Q3ListViewItem *);
+ void popupContextMenu(Q3ListViewItem *, const QPoint &, int);
+ void popupContextMenu(Q3ListBoxItem *, const QPoint &);
+ void updateFileNameEdit(Q3ListBoxItem *);
+ void selectDirectoryOrFile(Q3ListBoxItem *);
+ void fileNameEditDone();
+
+ void okClicked();
+ void filterClicked(); // not used
+ void cancelClicked();
+
+ void cdUpClicked();
+ void newFolderClicked();
+
+ void fixupNameEdit();
+
+ void doMimeTypeLookup();
+
+ void updateGeometries();
+ void modeButtonsDestroyed();
+ void urlStart(Q3NetworkOperation *op);
+ void urlFinished(Q3NetworkOperation *op);
+ void dataTransferProgress(int bytesDone, int bytesTotal, Q3NetworkOperation *);
+ void insertEntry(const Q3ValueList<QUrlInfo> &fi, Q3NetworkOperation *op);
+ void removeEntry(Q3NetworkOperation *);
+ void createdDirectory(const QUrlInfo &info, Q3NetworkOperation *);
+ void itemChanged(Q3NetworkOperation *);
+ void goBack();
+
+private:
+ Q_DISABLE_COPY(Q3FileDialog)
+
+ enum PopupAction {
+ PA_Open = 0,
+ PA_Delete,
+ PA_Rename,
+ PA_SortName,
+ PA_SortSize,
+ PA_SortType,
+ PA_SortDate,
+ PA_SortUnsorted,
+ PA_Cancel,
+ PA_Reload,
+ PA_Hidden
+ };
+
+ void init();
+ bool trySetSelection(bool isDir, const Q3UrlOperator &, bool);
+ void deleteFile(const QString &filename);
+ void popupContextMenu(const QString &filename, bool withSort,
+ PopupAction &action, const QPoint &p);
+ void updatePreviews(const Q3Url &u);
+
+ QString fileName;
+
+ friend class Q3FileDialogQFileListView;
+ friend class QFileListBox;
+
+ Q3FileDialogPrivate *d;
+ Q3FileDialogQFileListView *files;
+
+ QLineEdit *nameEdit; // also filter
+ QPushButton *okB;
+ QPushButton *cancelB;
+
+#if defined(Q_WS_WIN)
+ static QString winGetOpenFileName(const QString &initialSelection,
+ const QString &filter,
+ QString* workingDirectory,
+ QWidget *parent = 0,
+ const char* name = 0,
+ const QString& caption = QString(),
+ QString* selectedFilter = 0);
+ static QString winGetSaveFileName(const QString &initialSelection,
+ const QString &filter,
+ QString* workingDirectory,
+ QWidget *parent = 0,
+ const char* name = 0,
+ const QString& caption = QString(),
+ QString* selectedFilter = 0);
+ static QStringList winGetOpenFileNames(const QString &filter,
+ QString* workingDirectory,
+ QWidget *parent = 0,
+ const char* name = 0,
+ const QString& caption = QString(),
+ QString* selectedFilter = 0);
+ static QString winGetExistingDirectory(const QString &initialDirectory,
+ QWidget* parent = 0,
+ const char* name = 0,
+ const QString& caption = QString());
+ static QString resolveLinkFile(const QString& linkfile);
+ int old_qt_ntfs_permission_lookup;
+#endif
+#if defined(Q_WS_MAC)
+ static QString macGetSaveFileName(const QString &, const QString &,
+ QString *, QWidget *, const char*,
+ const QString&, QString *);
+ static QStringList macGetOpenFileNames(const QString &, QString*,
+ QWidget *, const char *,
+ const QString&, QString *,
+ bool = true, bool = false);
+#endif
+};
+
+#endif // QT_NO_FILEDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3FILEDIALOG_H
diff --git a/src/qt3support/dialogs/q3filedialog_mac.cpp b/src/qt3support/dialogs/q3filedialog_mac.cpp
new file mode 100644
index 0000000..0dcd014
--- /dev/null
+++ b/src/qt3support/dialogs/q3filedialog_mac.cpp
@@ -0,0 +1,594 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3filedialog.h"
+
+#ifndef QT_NO_FILEDIALOG
+
+/*****************************************************************************
+ Q3FileDialog debug facilities
+ *****************************************************************************/
+//#define DEBUG_FILEDIALOG_FILTERS
+
+#include "qapplication.h"
+#include <private/qapplication_p.h>
+#include <private/qt_mac_p.h>
+#include "qregexp.h"
+#include "qbuffer.h"
+#include "qstringlist.h"
+#include "qtextcodec.h"
+#include "qdesktopwidget.h"
+#include "qfiledialog.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_MAC_USE_COCOA
+
+QStringList Q3FileDialog::macGetOpenFileNames(const QString &filter, QString *pwd,
+ QWidget *parent, const char* /*name*/,
+ const QString& caption, QString *selectedFilter,
+ bool /*multi*/, bool /*directory*/)
+{
+ return QFileDialog::getOpenFileNames(filter, *pwd, parent, 0,
+ caption);
+}
+
+
+QString Q3FileDialog::macGetSaveFileName(const QString &start, const QString &filter,
+ QString *, QWidget *parent, const char* /*name*/,
+ const QString& caption, QString *selectedFilter)
+{
+ return QFileDialog::getSaveFileName(start, filter, parent, 0,
+ caption, selectedFilter);
+}
+
+#else
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern WindowPtr qt_mac_window_for(const QWidget*); //qwidget_mac.cpp
+extern const char qt3_file_dialog_filter_reg_exp[]; //qfiledialog.cpp
+
+/*****************************************************************************
+ Q3FileDialog utility functions
+ *****************************************************************************/
+static UInt8 *str_buffer = NULL;
+static void cleanup_str_buffer()
+{
+ if(str_buffer) {
+ free(str_buffer);
+ str_buffer = NULL;
+ }
+}
+
+// Returns the wildcard part of a filter.
+struct qt_mac_filter_name {
+ QString description, regxp, filter;
+};
+static qt_mac_filter_name *extractFilter(const QString& rawFilter)
+{
+ qt_mac_filter_name *ret = new qt_mac_filter_name;
+ ret->filter = rawFilter;
+ QString result = rawFilter;
+ QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp));
+ int index = r.indexIn(result);
+ if(index >= 0) {
+ ret->description = r.cap(1).trimmed();
+ result = r.cap(2);
+ }
+ if(ret->description.isEmpty())
+ ret->description = result;
+ ret->regxp = result.replace(QLatin1Char(' '), QLatin1Char(';'));
+ return ret;
+}
+
+// Makes a list of filters from ;;-separated text.
+static QList<qt_mac_filter_name*> makeFiltersList(const QString &filter)
+{
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("Q3FileDialog:%d - Got filter (%s)", __LINE__, filter.latin1());
+#endif
+ QString f(filter);
+ if(f.isEmpty())
+ f = Q3FileDialog::tr("All Files (*)");
+ if(f.isEmpty())
+ return QList<qt_mac_filter_name*>();
+ QString sep(QLatin1String(";;"));
+ int i = f.indexOf(sep, 0);
+ if(i == -1) {
+ sep = QLatin1String("\n");
+ if(f.indexOf(sep, 0) != -1)
+ i = f.indexOf(sep, 0);
+ }
+
+ QList<qt_mac_filter_name*> ret;
+ QStringList filts = f.split(sep);
+ for (QStringList::Iterator it = filts.begin(); it != filts.end(); ++it) {
+ qt_mac_filter_name *filter = extractFilter((*it));
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("Q3FileDialog:%d Split out filter (%d) '%s' '%s'", __LINE__, ret.count(),
+ filter->regxp.latin1(), filter->description.latin1());
+#endif
+ ret.append(filter);
+ }
+ return ret;
+}
+
+struct qt_mac_nav_filter_type {
+ int index;
+ QList<qt_mac_filter_name*> *filts;
+};
+
+static Boolean qt_mac_nav_filter(AEDesc *theItem, void *info,
+ void *myd, NavFilterModes)
+{
+ qt_mac_nav_filter_type *t = (qt_mac_nav_filter_type *)myd;
+ if(!t || !t->filts || t->index >= t->filts->count())
+ return true;
+
+ NavFileOrFolderInfo *theInfo = (NavFileOrFolderInfo *)info;
+ QString file;
+ qt_mac_filter_name *fn = t->filts->at(t->index);
+ if(!fn)
+ return true;
+ if(theItem->descriptorType == typeFSRef) {
+ FSRef ref;
+ AEGetDescData(theItem, &ref, sizeof(ref));
+ if(!str_buffer) {
+ qAddPostRoutine(cleanup_str_buffer);
+ str_buffer = (UInt8 *)malloc(1024);
+ }
+ FSRefMakePath(&ref, str_buffer, 1024);
+ file = QString::fromUtf8((const char *)str_buffer);
+ int slsh = file.lastIndexOf(QLatin1Char('/'));
+ if(slsh != -1)
+ file = file.right(file.length() - slsh - 1);
+ }
+ QStringList reg = fn->regxp.split(QLatin1String(";"));
+ for(QStringList::Iterator it = reg.begin(); it != reg.end(); ++it) {
+ QRegExp rg((*it), false, true);
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("Q3FileDialog:%d, asked to filter.. %s (%s)", __LINE__,
+ file.latin1(), (*it).latin1());
+#endif
+ if(rg.exactMatch(file))
+ return true;
+ }
+ return (theInfo->isFolder && !file.endsWith(QLatin1String(".app")));
+}
+
+//filter UPP stuff
+static NavObjectFilterUPP mac_navFilterUPP = NULL;
+static void cleanup_navFilterUPP()
+{
+ DisposeNavObjectFilterUPP(mac_navFilterUPP);
+ mac_navFilterUPP = NULL;
+}
+static const NavObjectFilterUPP make_navFilterUPP()
+{
+ if(mac_navFilterUPP)
+ return mac_navFilterUPP;
+ qAddPostRoutine(cleanup_navFilterUPP);
+ return mac_navFilterUPP = NewNavObjectFilterUPP(qt_mac_nav_filter);
+}
+//event UPP stuff
+static NavEventUPP mac_navProcUPP = NULL;
+static void cleanup_navProcUPP()
+{
+ DisposeNavEventUPP(mac_navProcUPP);
+ mac_navProcUPP = NULL;
+}
+static bool g_nav_blocking=true;
+static void qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg,
+ NavCBRecPtr p, NavCallBackUserData myd)
+{
+ switch(msg) {
+ case kNavCBPopupMenuSelect: {
+ qt_mac_nav_filter_type *t = (qt_mac_nav_filter_type *)myd;
+ NavMenuItemSpec *s = (NavMenuItemSpec*)p->eventData.eventDataParms.param;
+ t->index = s->menuType;
+#ifdef DEBUG_FILEDIALOG_FILTERS
+ qDebug("Q3FileDialog:%d - Selected a filter: %ld", __LINE__, s->menuType);
+#endif
+ break; }
+ case kNavCBStart:
+ g_nav_blocking=true;
+ break;
+ case kNavCBUserAction:
+ g_nav_blocking=false;
+ break;
+ }
+}
+static const NavEventUPP make_navProcUPP()
+{
+ if(mac_navProcUPP)
+ return mac_navProcUPP;
+ qAddPostRoutine(cleanup_navProcUPP);
+ return mac_navProcUPP = NewNavEventUPP(qt_mac_filedialog_event_proc);
+}
+
+
+extern OSErr qt_mac_create_fsref(const QString &, FSRef *); //qglobal.cpp
+
+QStringList Q3FileDialog::macGetOpenFileNames(const QString &filter, QString *pwd,
+ QWidget *parent, const char* /*name*/,
+ const QString& caption, QString *selectedFilter,
+ bool multi, bool directory)
+{
+ OSErr err;
+ QStringList retstrl;
+
+ NavDialogCreationOptions options;
+ NavGetDefaultDialogCreationOptions(&options);
+ options.modality = kWindowModalityAppModal;
+ options.optionFlags |= kNavDontConfirmReplacement | kNavSupportPackages;
+ if (!multi)
+ options.optionFlags &= ~kNavAllowMultipleFiles;
+ if(!caption.isEmpty())
+ options.windowTitle = CFStringCreateWithCharacters(NULL, (UniChar *)caption.unicode(),
+ caption.length());
+
+ static const int w = 450, h = 350;
+ options.location.h = options.location.v = -1;
+ if(parent && parent->isVisible()) {
+ Qt::WindowType wt = parent->window()->windowType();
+ if (wt != Qt::Desktop && wt != Qt::Sheet && wt != Qt::Drawer) {
+ options.modality = kWindowModalityWindowModal;
+ options.parentWindow = qt_mac_window_for(parent);
+ } else {
+ parent = parent->window();
+ QString s = parent->windowTitle();
+ options.clientName = CFStringCreateWithCharacters(NULL, (UniChar *)s.unicode(), s.length());
+ options.location.h = (parent->x() + (parent->width() / 2)) - (w / 2);
+ options.location.v = (parent->y() + (parent->height() / 2)) - (h / 2);
+
+ QRect r = QApplication::desktop()->screenGeometry(
+ QApplication::desktop()->screenNumber(parent));
+ if(options.location.h + w > r.right())
+ options.location.h -= (options.location.h + w) - r.right() + 10;
+ if(options.location.v + h > r.bottom())
+ options.location.v -= (options.location.v + h) - r.bottom() + 10;
+ }
+ } else if(QWidget *p = qApp->mainWidget()) {
+ static int last_screen = -1;
+ int scr = QApplication::desktop()->screenNumber(p);
+ if(last_screen != scr) {
+ QRect r = QApplication::desktop()->screenGeometry(scr);
+ options.location.h = (r.x() + (r.width() / 2)) - (w / 2);
+ options.location.v = (r.y() + (r.height() / 2)) - (h / 2);
+ }
+ }
+
+ QList<qt_mac_filter_name*> filts = makeFiltersList(filter);
+ qt_mac_nav_filter_type t;
+ t.index = 0;
+ t.filts = &filts;
+ if(filts.count() > 1) {
+ int i = 0;
+ CFStringRef *arr = (CFStringRef *)malloc(sizeof(CFStringRef) * filts.count());
+ for (QList<qt_mac_filter_name*>::Iterator it = filts.begin(); it != filts.end(); ++it) {
+ QString rg = (*it)->description;
+ arr[i++] = CFStringCreateWithCharacters(NULL, (UniChar *)rg.unicode(), rg.length());
+ }
+ options.popupExtension = CFArrayCreate(NULL, (const void **)arr, filts.count(), NULL);
+ }
+
+ NavDialogRef dlg;
+ if(directory) {
+ if(NavCreateChooseFolderDialog(&options, make_navProcUPP(), NULL, NULL, &dlg)) {
+ qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+ return retstrl;
+ }
+ } else {
+ if(NavCreateGetFileDialog(&options, NULL, make_navProcUPP(), NULL,
+ make_navFilterUPP(), (void *) (filts.isEmpty() ? NULL : &t),
+ &dlg)) {
+ qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+ return retstrl;
+ }
+ }
+ if(pwd && !pwd->isEmpty()) {
+ FSRef fsref;
+ if(qt_mac_create_fsref(*pwd, &fsref) == noErr) {
+ AEDesc desc;
+ if(AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc) == noErr)
+ NavCustomControl(dlg, kNavCtlSetLocation, (void*)&desc);
+ }
+ }
+
+ NavDialogRun(dlg);
+ if (selectedFilter) {
+ NavMenuItemSpec navSpec;
+ bzero(&navSpec, sizeof(NavMenuItemSpec));
+ qt_mac_filter_name *sel_filt_name = makeFiltersList(*selectedFilter).at(0);
+ for (int i = 0; i < filts.count(); ++i) {
+ const qt_mac_filter_name *filter = filts.at(i);
+ if (sel_filt_name->description == filter->description
+ && sel_filt_name->regxp == filter->regxp
+ && sel_filt_name->filter == filter->filter) {
+ navSpec.menuType = i;
+ break;
+ }
+ }
+ NavCustomControl(dlg, kNavCtlSelectCustomType, &navSpec);
+ }
+ if(options.modality == kWindowModalityWindowModal) { //simulate modality
+ QWidget modal_widg(parent, __FILE__ "__modal_dlg",
+ Qt::WType_TopLevel | Qt::WStyle_Customize | Qt::WStyle_DialogBorder);
+ modal_widg.createWinId();
+ QApplicationPrivate::enterModal(&modal_widg);
+ while(g_nav_blocking)
+ qApp->processEvents(QEventLoop::WaitForMoreEvents);
+ QApplicationPrivate::leaveModal(&modal_widg);
+ }
+
+ if(!(NavDialogGetUserAction(dlg) &
+ (kNavUserActionOpen | kNavUserActionChoose | kNavUserActionNewFolder))) {
+ NavDialogDispose(dlg);
+ return retstrl;
+ }
+ NavReplyRecord ret;
+ NavDialogGetReply(dlg, &ret);
+ NavDialogDispose(dlg);
+
+ long count;
+ err = AECountItems(&(ret.selection), &count);
+ if(!ret.validRecord || err != noErr || !count) {
+ NavDisposeReply(&ret);
+ return retstrl;
+ }
+
+ for(long index = 1; index <= count; index++) {
+ FSRef ref;
+ err = AEGetNthPtr(&(ret.selection), index, typeFSRef, 0, 0, &ref, sizeof(ref), 0);
+ if(err != noErr)
+ break;
+
+ if(!str_buffer) {
+ qAddPostRoutine(cleanup_str_buffer);
+ str_buffer = (UInt8 *)malloc(1024);
+ }
+ FSRefMakePath(&ref, str_buffer, 1024);
+ retstrl.append(QString::fromUtf8((const char *)str_buffer));
+ }
+ NavDisposeReply(&ret);
+ if(selectedFilter)
+ *selectedFilter = filts.at(t.index)->filter;
+ while (!filts.isEmpty())
+ delete filts.takeFirst();
+ return retstrl;
+}
+
+// Copious copy and paste from qfiledialog.cpp. Fix in 4.0.
+static QString encodeFileName(const QString &fName)
+{
+ QString newStr;
+ QByteArray cName = fName.utf8();
+ const QByteArray sChars("<>#@\"&%$:,;?={}|^~[]\'`\\*");
+
+ int len = cName.length();
+ if (!len)
+ return QString();
+ for (int i = 0; i < len ;++i) {
+ uchar inCh = (uchar)cName[i];
+ if (inCh >= 128 || sChars.contains(inCh))
+ {
+ newStr += QLatin1Char('%');
+ ushort c = inCh / 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newStr += QLatin1Char((char)c);
+ c = inCh % 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newStr += QLatin1Char((char)c);
+ } else {
+ newStr += QLatin1Char((char)inCh);
+ }
+ }
+ return newStr;
+}
+
+QString Q3FileDialog::macGetSaveFileName(const QString &start, const QString &filter,
+ QString *, QWidget *parent, const char* /*name*/,
+ const QString& caption, QString *selectedFilter)
+{
+ OSErr err;
+ QString retstr;
+ NavDialogCreationOptions options;
+ NavGetDefaultDialogCreationOptions(&options);
+ static const int w = 450, h = 350;
+ options.optionFlags |= kNavDontConfirmReplacement;
+ options.modality = kWindowModalityAppModal;
+ options.location.h = options.location.v = -1;
+ QString workingDir;
+ QString initialSelection;
+ if (!start.isEmpty()) {
+ Q3UrlOperator u(encodeFileName(start));
+ if (u.isLocalFile() && QFileInfo(u.path()).isDir()) {
+ workingDir = start;
+ } else {
+ if (u.isLocalFile()) {
+ QFileInfo fi(u.dirPath());
+ if (fi.exists()) {
+ workingDir = u.dirPath();
+ initialSelection = u.fileName();
+ }
+ } else {
+ workingDir = u.toString();
+ }
+ }
+ if (!initialSelection.isEmpty())
+ options.saveFileName = CFStringCreateWithCharacters(0,
+ (UniChar *)initialSelection.unicode(),
+ initialSelection.length());
+ }
+ if(!caption.isEmpty())
+ options.windowTitle = CFStringCreateWithCharacters(NULL, (UniChar *)caption.unicode(),
+ caption.length());
+ if(parent && parent->isVisible()) {
+ Qt::WindowType wt = parent->window()->windowType();
+ if (wt != Qt::Desktop && wt != Qt::Sheet && wt != Qt::Drawer) {
+ options.modality = kWindowModalityWindowModal;
+ options.parentWindow = qt_mac_window_for(parent);
+ } else {
+ parent = parent->window();
+ QString s = parent->windowTitle();
+ options.clientName = CFStringCreateWithCharacters(NULL, (UniChar *)s.unicode(), s.length());
+ options.location.h = (parent->x() + (parent->width() / 2)) - (w / 2);
+ options.location.v = (parent->y() + (parent->height() / 2)) - (h / 2);
+
+ QRect r = QApplication::desktop()->screenGeometry(
+ QApplication::desktop()->screenNumber(parent));
+ if(options.location.h + w > r.right())
+ options.location.h -= (options.location.h + w) - r.right() + 10;
+ if(options.location.v + h > r.bottom())
+ options.location.v -= (options.location.v + h) - r.bottom() + 10;
+ }
+ } else if(QWidget *p = qApp->mainWidget()) {
+ static int last_screen = -1;
+ int scr = QApplication::desktop()->screenNumber(p);
+ if(last_screen != scr) {
+ QRect r = QApplication::desktop()->screenGeometry(scr);
+ options.location.h = (r.x() + (r.width() / 2)) - (w / 2);
+ options.location.v = (r.y() + (r.height() / 2)) - (h / 2);
+ }
+ }
+
+ QList<qt_mac_filter_name*> filts = makeFiltersList(filter);
+ qt_mac_nav_filter_type t;
+ t.index = 0;
+ t.filts = &filts;
+ if(filts.count() > 1) {
+ int i = 0;
+ CFStringRef *arr = (CFStringRef *)malloc(sizeof(CFStringRef) * filts.count());
+ for (QList<qt_mac_filter_name*>::Iterator it = filts.begin(); it != filts.end(); ++it) {
+ QString rg = (*it)->description;
+ arr[i++] = CFStringCreateWithCharacters(NULL, (UniChar *)rg.unicode(), rg.length());
+ }
+ options.popupExtension = CFArrayCreate(NULL, (const void **)arr, filts.count(), NULL);
+ }
+
+ NavDialogRef dlg;
+ if(NavCreatePutFileDialog(&options, 'cute', kNavGenericSignature, make_navProcUPP(),
+ (void *) (filts.isEmpty() ? NULL : &t), &dlg)) {
+ qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+ return retstr;
+ }
+ if (!workingDir.isEmpty()) {
+ FSRef fsref;
+ if (qt_mac_create_fsref(workingDir, &fsref) == noErr) {
+ AEDesc desc;
+ if (AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc) == noErr)
+ NavCustomControl(dlg, kNavCtlSetLocation, (void*)&desc);
+ }
+ }
+ NavDialogRun(dlg);
+ if (selectedFilter) {
+ NavMenuItemSpec navSpec;
+ bzero(&navSpec, sizeof(NavMenuItemSpec));
+ qt_mac_filter_name *sel_filt_name = makeFiltersList(*selectedFilter).at(0);
+ for (int i = 0; i < filts.count(); ++i) {
+ const qt_mac_filter_name *filter = filts.at(i);
+ if (sel_filt_name->description == filter->description
+ && sel_filt_name->regxp == filter->regxp
+ && sel_filt_name->filter == filter->filter) {
+ navSpec.menuType = i;
+ break;
+ }
+ }
+ NavCustomControl(dlg, kNavCtlSelectCustomType, &navSpec);
+ }
+ if(options.modality == kWindowModalityWindowModal) { //simulate modality
+ QWidget modal_widg(parent, __FILE__ "__modal_dlg",
+ Qt::WType_TopLevel | Qt::WStyle_Customize | Qt::WStyle_DialogBorder);
+ modal_widg.createWinId();
+ QApplicationPrivate::enterModal(&modal_widg);
+ while(g_nav_blocking)
+ qApp->processEvents(QEventLoop::WaitForMoreEvents);
+ QApplicationPrivate::leaveModal(&modal_widg);
+ }
+
+ if(NavDialogGetUserAction(dlg) != kNavUserActionSaveAs) {
+ NavDialogDispose(dlg);
+ return retstr;
+ }
+ NavReplyRecord ret;
+ NavDialogGetReply(dlg, &ret);
+ NavDialogDispose(dlg);
+
+ long count;
+ err = AECountItems(&(ret.selection), &count);
+ if(!ret.validRecord || err != noErr || !count) {
+ NavDisposeReply(&ret);
+ return retstr;
+ }
+
+ AEKeyword keyword;
+ DescType type;
+ Size size;
+ FSRef ref;
+ err = AEGetNthPtr(&(ret.selection), 1, typeFSRef, &keyword,
+ &type, &ref, sizeof(ref), &size);
+ if(err == noErr) {
+ if(!str_buffer) {
+ qAddPostRoutine(cleanup_str_buffer);
+ str_buffer = (UInt8 *)malloc(1024);
+ }
+ FSRefMakePath(&ref, str_buffer, 1024);
+ retstr = QString::fromUtf8((const char *)str_buffer);
+ //now filename
+ CFStringGetCString(ret.saveFileName, (char *)str_buffer, 1024, kCFStringEncodingUTF8);
+ retstr += QLatin1Char('/') + QString::fromUtf8((const char *)str_buffer);
+ }
+ NavDisposeReply(&ret);
+ if(selectedFilter)
+ *selectedFilter = filts.at(t.index)->filter;
+ while (!filts.isEmpty())
+ delete filts.takeFirst();
+ return retstr;
+}
+
+#endif // QT_MAC_USE_COCOA
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/dialogs/q3filedialog_win.cpp b/src/qt3support/dialogs/q3filedialog_win.cpp
new file mode 100644
index 0000000..25783db
--- /dev/null
+++ b/src/qt3support/dialogs/q3filedialog_win.cpp
@@ -0,0 +1,513 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3filedialog.h"
+
+#ifndef QT_NO_FILEDIALOG
+
+#include "qapplication.h"
+#include "private/qapplication_p.h"
+#include "qt_windows.h"
+#include "qregexp.h"
+#include "qbuffer.h"
+#include "qdir.h"
+#include "qstringlist.h"
+#include "qlibrary.h"
+
+#ifndef QT_NO_THREAD
+# include "private/qmutexpool_p.h"
+#endif
+
+#include <shlobj.h>
+
+#ifdef Q_OS_WINCE
+#include <commdlg.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern const char qt3_file_dialog_filter_reg_exp[]; // defined in qfiledialog.cpp
+
+const int maxNameLen = 1023;
+const int maxMultiLen = 65535;
+
+// Returns the wildcard part of a filter.
+static QString extractFilter(const QString& rawFilter)
+{
+ QString result = rawFilter;
+ QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp));
+ int index = r.indexIn(result);
+ if (index >= 0)
+ result = r.cap(2);
+ return result.replace(QLatin1Char(' '), QLatin1Char(';'));
+}
+
+// Makes a list of filters from ;;-separated text.
+static QStringList makeFiltersList(const QString &filter)
+{
+ QString f(filter);
+
+ if (f.isEmpty())
+ f = Q3FileDialog::tr("All Files (*.*)");
+
+ if (f.isEmpty())
+ return QStringList();
+
+ int i = f.find(QLatin1String(";;"), 0);
+ QString sep(QLatin1String(";;"));
+ if (i == -1) {
+ if (f.find(QLatin1String("\n"), 0) != -1) {
+ sep = QLatin1String("\n");
+ i = f.find(sep, 0);
+ }
+ }
+
+ return QStringList::split(sep, f );
+}
+
+// Makes a NUL-oriented Windows filter from a Qt filter.
+static QString winFilter(const QString& filter)
+{
+ QStringList filterLst = makeFiltersList(filter);
+ QStringList::Iterator it = filterLst.begin();
+ QString winfilters;
+ for (; it != filterLst.end(); ++it) {
+ winfilters += *it;
+ winfilters += QChar::null;
+ winfilters += extractFilter(*it);
+ winfilters += QChar::null;
+ }
+ winfilters += QChar::null;
+ return winfilters;
+}
+
+static QString selFilter(const QString& filter, DWORD idx)
+{
+ QStringList filterLst = makeFiltersList(filter);
+ return filterLst[(int)idx - 1];
+}
+
+static QString tFilters, tTitle, tInitDir;
+
+static
+OPENFILENAME* makeOFN(QWidget* parent,
+ const QString& initialSelection,
+ const QString& initialDirectory,
+ const QString& title,
+ const QString& filters,
+ Q3FileDialog::Mode mode)
+{
+ if (parent)
+ parent = parent->window();
+ else
+ parent = qApp->activeWindow();
+
+ tInitDir = QDir::toNativeSeparators(initialDirectory);
+ tFilters = filters;
+ tTitle = title;
+ QString initSel = QDir::toNativeSeparators(initialSelection);
+
+ int maxLen = mode == Q3FileDialog::ExistingFiles ? maxMultiLen : maxNameLen;
+ wchar_t *tInitSel = new wchar_t[maxLen+1];
+ if (initSel.length() > 0 && initSel.length() <= maxLen)
+ memcpy(tInitSel, initSel.utf16(), (initSel.length() + 1) * sizeof(wchar_t));
+ else
+ tInitSel[0] = 0;
+
+ OPENFILENAME* ofn = new OPENFILENAME;
+ memset(ofn, 0, sizeof(OPENFILENAME));
+
+ ofn->lStructSize = sizeof(OPENFILENAME);
+ ofn->hwndOwner = parent ? parent->winId() : 0;
+ ofn->lpstrFilter = (wchar_t*)tFilters.utf16();
+ ofn->lpstrFile = tInitSel;
+ ofn->nMaxFile = maxLen;
+ ofn->lpstrInitialDir = (wchar_t*)tInitDir.utf16();
+ ofn->lpstrTitle = (wchar_t*)tTitle.utf16();
+ ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY);
+
+ if (mode == Q3FileDialog::ExistingFile ||
+ mode == Q3FileDialog::ExistingFiles)
+ ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
+ if (mode == Q3FileDialog::ExistingFiles)
+ ofn->Flags |= (OFN_ALLOWMULTISELECT | OFN_EXPLORER);
+
+ return ofn;
+}
+
+static void cleanUpOFN(OPENFILENAME** ofn)
+{
+ delete (*ofn)->lpstrFile;
+ delete *ofn;
+ *ofn = 0;
+}
+
+QString Q3FileDialog::winGetOpenFileName(const QString &initialSelection,
+ const QString &filter,
+ QString* initialDirectory,
+ QWidget *parent, const char* /*name*/,
+ const QString& caption,
+ QString* selectedFilter)
+{
+ QString result;
+
+ QString isel = initialSelection;
+
+ if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
+ initialDirectory->remove(0, 5);
+ QFileInfo fi(*initialDirectory);
+
+ if (initialDirectory && !fi.isDir()) {
+ *initialDirectory = fi.dirPath(true);
+ if (isel.isEmpty())
+ isel = fi.fileName();
+ }
+
+ if (!fi.exists())
+ *initialDirectory = QDir::homeDirPath();
+
+ QString title = caption;
+ if (title.isNull())
+ title = tr("Open");
+
+ DWORD selFilIdx = 0;
+
+ int idx = 0;
+ if (selectedFilter && !selectedFilter->isEmpty()) {
+ QStringList filterLst = makeFiltersList(filter);
+ idx = filterLst.findIndex(*selectedFilter);
+ }
+
+ if (parent) {
+ QEvent e(QEvent::WindowBlocked);
+ QApplication::sendEvent(parent, &e);
+ QApplicationPrivate::enterModal(parent);
+ }
+
+ OPENFILENAME* ofn = makeOFN(parent, isel,
+ *initialDirectory, title,
+ winFilter(filter), ExistingFile);
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetOpenFileName(ofn)) {
+ result = QString::fromWCharArray(ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ }
+ cleanUpOFN(&ofn);
+
+ if (parent) {
+ QApplicationPrivate::leaveModal(parent);
+ QEvent e(QEvent::WindowUnblocked);
+ QApplication::sendEvent(parent, &e);
+ }
+
+ if (result.isEmpty()) {
+ return result;
+ }
+ else {
+ QFileInfo fi(result);
+ *initialDirectory = fi.dirPath();
+ if (selectedFilter)
+ *selectedFilter = selFilter(filter, selFilIdx);
+ return fi.absFilePath();
+ }
+}
+
+
+QString Q3FileDialog::winGetSaveFileName(const QString &initialSelection,
+ const QString &filter,
+ QString* initialDirectory,
+ QWidget *parent, const char* /*name*/,
+ const QString& caption,
+ QString* selectedFilter)
+{
+ QString result;
+
+ QString isel = initialSelection;
+ if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
+ initialDirectory->remove(0, 5);
+ QFileInfo fi(*initialDirectory);
+
+ if (initialDirectory && !fi.isDir()) {
+ *initialDirectory = fi.dirPath(true);
+ if (isel.isEmpty())
+ isel = fi.fileName();
+ }
+
+ if (!fi.exists())
+ *initialDirectory = QDir::homeDirPath();
+
+ QString title = caption;
+ if (title.isNull())
+ title = tr("Save As");
+
+ DWORD selFilIdx = 0;
+
+ int idx = 0;
+ if (selectedFilter && !selectedFilter->isEmpty()) {
+ QStringList filterLst = makeFiltersList(filter);
+ idx = filterLst.findIndex(*selectedFilter);
+ }
+
+ if (parent) {
+ QEvent e(QEvent::WindowBlocked);
+ QApplication::sendEvent(parent, &e);
+ QApplicationPrivate::enterModal(parent);
+ }
+
+ OPENFILENAME* ofn = makeOFN(parent, isel,
+ *initialDirectory, title,
+ winFilter(filter), AnyFile);
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetSaveFileName(ofn)) {
+ result = QString::fromWCharArray(ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ }
+ cleanUpOFN(&ofn);
+
+ if (parent) {
+ QApplicationPrivate::leaveModal(parent);
+ QEvent e(QEvent::WindowUnblocked);
+ QApplication::sendEvent(parent, &e);
+ }
+
+ if (result.isEmpty()) {
+ return result;
+ }
+ else {
+ QFileInfo fi(result);
+ *initialDirectory = fi.dirPath();
+ if (selectedFilter)
+ *selectedFilter = selFilter(filter, selFilIdx);
+ return fi.absFilePath();
+ }
+}
+
+
+
+QStringList Q3FileDialog::winGetOpenFileNames(const QString &filter,
+ QString* initialDirectory,
+ QWidget *parent,
+ const char* /*name*/,
+ const QString& caption,
+ QString* selectedFilter)
+{
+ QStringList result;
+ QFileInfo fi;
+ QDir dir;
+ QString isel;
+
+ if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
+ initialDirectory->remove(0, 5);
+ fi = QFileInfo(*initialDirectory);
+
+ if (initialDirectory && !fi.isDir()) {
+ *initialDirectory = fi.dirPath(true);
+ isel = fi.fileName();
+ }
+
+ if (!fi.exists())
+ *initialDirectory = QDir::homeDirPath();
+
+ QString title = caption;
+ if (title.isNull())
+ title = tr("Open ");
+
+ DWORD selFilIdx = 0;
+
+ int idx = 0;
+ if (selectedFilter && !selectedFilter->isEmpty()) {
+ QStringList filterLst = makeFiltersList(filter);
+ idx = filterLst.findIndex(*selectedFilter);
+ }
+
+ if (parent) {
+ QEvent e(QEvent::WindowBlocked);
+ QApplication::sendEvent(parent, &e);
+ QApplicationPrivate::enterModal(parent);
+ }
+
+ OPENFILENAME* ofn = makeOFN(parent, isel,
+ *initialDirectory, title,
+ winFilter(filter), ExistingFiles);
+ if (idx)
+ ofn->nFilterIndex = idx + 1;
+ if (GetOpenFileName(ofn)) {
+ QString fileOrDir = QString::fromWCharArray(ofn->lpstrFile);
+ selFilIdx = ofn->nFilterIndex;
+ int offset = fileOrDir.length() + 1;
+ if (ofn->lpstrFile[offset] == 0) {
+ // Only one file selected; has full path
+ fi.setFile(fileOrDir);
+ QString res = fi.absFilePath();
+ if (!res.isEmpty())
+ result.append(res);
+ }
+ else {
+ // Several files selected; first string is path
+ dir.setPath(fileOrDir);
+ QString f;
+ while (!(f = QString::fromWCharArray(ofn->lpstrFile + offset)).isEmpty()) {
+ fi.setFile(dir, f);
+ QString res = fi.absFilePath();
+ if (!res.isEmpty())
+ result.append(res);
+ offset += f.length() + 1;
+ }
+ }
+ }
+ cleanUpOFN(&ofn);
+
+ if (parent) {
+ QApplicationPrivate::leaveModal(parent);
+ QEvent e(QEvent::WindowUnblocked);
+ QApplication::sendEvent(parent, &e);
+ }
+
+ if (!result.isEmpty()) {
+ *initialDirectory = fi.dirPath(); // only save the path if there is a result
+ if (selectedFilter)
+ *selectedFilter = selFilter(filter, selFilIdx);
+ }
+ return result;
+}
+
+// MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers)
+
+static int __stdcall winGetExistDirCallbackProc(HWND hwnd,
+ UINT uMsg,
+ LPARAM lParam,
+ LPARAM lpData)
+{
+#ifndef Q_OS_WINCE
+ if (uMsg == BFFM_INITIALIZED && lpData != 0) {
+ QString *initDir = (QString *)(lpData);
+ if (!initDir->isEmpty()) {
+ SendMessage(hwnd, BFFM_SETSELECTION, TRUE, Q_ULONG(initDir->utf16()));
+ }
+ } else if (uMsg == BFFM_SELCHANGED) {
+ wchar_t path[MAX_PATH];
+ SHGetPathFromIDList(LPITEMIDLIST(lParam), path);
+ QString tmpStr = QString::fromWCharArray(path);
+ if (!tmpStr.isEmpty())
+ SendMessage(hwnd, BFFM_ENABLEOK, 1, 1);
+ else
+ SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
+ SendMessage(hwnd, BFFM_SETSTATUSTEXT, 1, Q_ULONG(path));
+ }
+#endif
+ return 0;
+}
+
+#ifndef BIF_NEWDIALOGSTYLE
+#define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize
+#endif
+
+
+QString Q3FileDialog::winGetExistingDirectory(const QString& initialDirectory,
+ QWidget *parent,
+ const char* /*name*/,
+ const QString& caption)
+{
+#ifndef Q_OS_WINCE
+ QString currentDir = QDir::currentDirPath();
+ QString result;
+ if (parent)
+ parent = parent->window();
+ else
+ parent = qApp->activeWindow();
+ QString title = caption;
+ if (title.isNull())
+ title = tr("Select a Directory");
+
+ if (parent) {
+ QEvent e(QEvent::WindowBlocked);
+ QApplication::sendEvent(parent, &e);
+ QApplicationPrivate::enterModal(parent);
+ }
+
+ QString initDir = QDir::toNativeSeparators(initialDirectory);
+ wchar_t path[MAX_PATH];
+ wchar_t initPath[MAX_PATH];
+ initPath[0] = 0;
+ path[0] = 0;
+ tTitle = title;
+ BROWSEINFO bi;
+ bi.hwndOwner = (parent ? parent->winId() : 0);
+ bi.pidlRoot = NULL;
+ bi.lpszTitle = (wchar_t*)tTitle.utf16();
+ bi.pszDisplayName = initPath;
+ bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
+ bi.lpfn = winGetExistDirCallbackProc;
+ bi.lParam = Q_ULONG(&initDir);
+ LPITEMIDLIST pItemIDList = SHBrowseForFolder(&bi);
+ if (pItemIDList) {
+ SHGetPathFromIDList(pItemIDList, path);
+ IMalloc *pMalloc;
+ if (SHGetMalloc(&pMalloc) != NOERROR)
+ result.clear();
+ else {
+ pMalloc->Free(pItemIDList);
+ pMalloc->Release();
+ result = QString::fromWCharArray(path);
+ }
+ } else
+ result.clear();
+ tTitle.clear();
+
+ if (parent) {
+ QApplicationPrivate::leaveModal(parent);
+ QEvent e(QEvent::WindowUnblocked);
+ QApplication::sendEvent(parent, &e);
+ }
+
+ if (!result.isEmpty())
+ result.replace(QLatin1Char('\\'), QLatin1Char('/'));
+ return result;
+#else
+ return QString();
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/dialogs/q3progressdialog.cpp b/src/qt3support/dialogs/q3progressdialog.cpp
new file mode 100644
index 0000000..2ef9112
--- /dev/null
+++ b/src/qt3support/dialogs/q3progressdialog.cpp
@@ -0,0 +1,850 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3progressdialog.h"
+
+#ifndef QT_NO_PROGRESSDIALOG
+
+#include "q3progressbar.h"
+#include "qapplication.h"
+#include "qcursor.h"
+#include "qdatetime.h"
+#include "qlabel.h"
+#include "qpainter.h"
+#include "qpushbutton.h"
+#include "qshortcut.h"
+#include "qstyle.h"
+#include "qtimer.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+// If the operation is expected to take this long (as predicted by
+// progress time), show the progress dialog.
+static const int defaultShowTime = 4000;
+// Wait at least this long before attempting to make a prediction.
+static const int minWaitTime = 50;
+
+// Various layout values
+static const int margin_lr = 10;
+static const int margin_tb = 10;
+static const int spacing = 4;
+
+
+class Q3ProgressDialogData
+{
+public:
+ Q3ProgressDialogData(Q3ProgressDialog* that, QWidget* parent,
+ const QString& labelText,
+ int totalSteps) :
+ creator(parent),
+ label(new QLabel(labelText,that)),
+ cancel(0),
+ bar(new Q3ProgressBar(totalSteps, that)),
+ shown_once(false),
+ cancellation_flag(false),
+ showTime(defaultShowTime)
+ {
+ int align = that->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, that);
+ label->setAlignment(Qt::Alignment(align));
+ }
+
+ QWidget *creator;
+ QLabel *label;
+ QPushButton *cancel;
+ Q3ProgressBar *bar;
+ bool shown_once;
+ bool cancellation_flag;
+ QTime starttime;
+#ifndef QT_NO_CURSOR
+ QCursor parentCursor;
+#endif
+ int showTime;
+ bool autoClose;
+ bool autoReset;
+ bool forceHide;
+};
+
+
+/*!
+ \class Q3ProgressDialog
+ \brief The Q3ProgressDialog class provides feedback on the progress of a slow operation.
+
+ \compat
+
+ A progress dialog is used to give the user an indication of how long
+ an operation is going to take, and to demonstrate that the
+ application has not frozen. It can also give the user an opportunity
+ to abort the operation.
+
+ A common problem with progress dialogs is that it is difficult to know
+ when to use them; operations take different amounts of time on different
+ hardware. Q3ProgressDialog offers a solution to this problem:
+ it estimates the time the operation will take (based on time for
+ steps), and only shows itself if that estimate is beyond minimumDuration()
+ (4 seconds by default).
+
+ Use setTotalSteps() (or the constructor) to set the number of
+ "steps" in the operation and call setProgress() as the operation
+ progresses. The step value can be chosen arbitrarily. It can be the
+ number of files copied, the number of bytes received, the number of
+ iterations through the main loop of your algorithm, or some other
+ suitable unit. Progress starts at 0, and the progress dialog shows
+ that the operation has finished when you call setProgress() with
+ totalSteps() as its argument.
+
+ The dialog automatically resets and hides itself at the end of the
+ operation. Use setAutoReset() and setAutoClose() to change this
+ behavior.
+
+ There are two ways of using Q3ProgressDialog: modal and modeless.
+
+ Using a modal Q3ProgressDialog is simpler for the programmer, but you
+ must call QApplication::processEvents() or
+ QEventLoop::processEvents(ExcludeUserInput) to keep the event loop
+ running to ensure that the application doesn't freeze. Do the
+ operation in a loop, call \l setProgress() at intervals, and check
+ for cancellation with wasCanceled(). For example:
+\snippet doc/src/snippets/code/src_qt3support_dialogs_q3progressdialog.cpp 0
+
+ A modeless progress dialog is suitable for operations that take
+ place in the background, where the user is able to interact with the
+ application. Such operations are typically based on QTimer (or
+ QObject::timerEvent()), QSocketNotifier, or QUrlOperator; or performed
+ in a separate thread. A Q3ProgressBar in the status bar of your main window
+ is often an alternative to a modeless progress dialog.
+
+ You need to have an event loop to be running, connect the
+ canceled() signal to a slot that stops the operation, and call \l
+ setProgress() at intervals. For example:
+\snippet doc/src/snippets/code/src_qt3support_dialogs_q3progressdialog.cpp 1
+
+
+ In both modes the progress dialog may be customized by
+ replacing the child widgets with custom widgets by using setLabel(),
+ setBar(), and setCancelButton().
+ The functions setLabelText() and setCancelButtonText()
+ set the texts shown.
+
+ \inlineimage qprogdlg-m.png Screenshot in Motif style
+ \inlineimage qprogdlg-w.png Screenshot in Windows style
+
+ \sa QDialog, Q3ProgressBar, {fowler}{GUI Design Handbook: Progress Indicator}
+*/
+
+
+/*!
+ Returns the QLabel currently being displayed above the progress bar.
+ This QLabel is owned by the Q3ProgressDialog.
+
+ \sa setLabel()
+*/
+QLabel *Q3ProgressDialog::label() const
+{
+ return d->label;
+}
+
+/*!
+ Returns the Q3ProgressBar currently being used to display progress.
+ This Q3ProgressBar is owned by the Q3ProgressDialog.
+
+ \sa setBar()
+*/
+Q3ProgressBar *Q3ProgressDialog::bar() const
+{
+ return d->bar;
+}
+
+
+/*!
+ Constructs a progress dialog.
+
+ Default settings:
+ \list
+ \i The label text is empty.
+ \i The cancel button text is (translated) "Cancel".
+ \i The total number of steps is 100.
+ \endlist
+
+ The \a creator argument is the widget to use as the dialog's parent.
+ The \a name, \a modal, and the widget flags, \a f, are
+ passed to the QDialog::QDialog() constructor. If \a modal is false (the
+ default), you must have an event loop proceeding for any redrawing
+ of the dialog to occur. If \a modal is true, the dialog ensures that
+ events are processed when needed.
+
+ \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
+ setTotalSteps()
+*/
+
+Q3ProgressDialog::Q3ProgressDialog(QWidget *creator, const char *name,
+ bool modal, Qt::WindowFlags f)
+ : QDialog(creator, f)
+{
+ setObjectName(QLatin1String(name));
+ setModal(modal);
+ init(creator, QString::fromLatin1(""), tr("Cancel"), 100);
+}
+
+/*!
+ Constructs a progress dialog.
+
+ The \a labelText is text used to remind the user what is progressing.
+
+ The \a cancelButtonText is the text to display on the cancel button,
+ or 0 if no cancel button is to be shown.
+
+ The \a totalSteps is the total number of steps in the operation for
+ which this progress dialog shows progress. For example, if the
+ operation is to examine 50 files, this value would be 50. Before
+ examining the first file, call setProgress(0). As each file is
+ processed call setProgress(1), setProgress(2), etc., finally
+ calling setProgress(50) after examining the last file.
+
+ The \a creator argument is the widget to use as the dialog's parent.
+ The \a name, \a modal, and widget flags, \a f, are passed to the
+ QDialog::QDialog() constructor. If \a modal is false (the default),
+ you will must have an event loop proceeding for any redrawing of
+ the dialog to occur. If \a modal is true, the dialog ensures that
+ events are processed when needed.
+
+
+ \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
+ setTotalSteps()
+*/
+
+Q3ProgressDialog::Q3ProgressDialog(const QString &labelText,
+ const QString &cancelButtonText,
+ int totalSteps,
+ QWidget *creator, const char *name,
+ bool modal, Qt::WindowFlags f)
+ : QDialog(creator, f)
+{
+ setObjectName(QLatin1String(name));
+ setModal(modal);
+ init(creator, labelText, cancelButtonText, totalSteps);
+}
+
+/*!
+ Constructs a progress dialog.
+
+ Default settings:
+ \list
+ \i The label text is empty.
+ \i The cancel button text is (translated) "Cancel".
+ \i The total number of steps is 100.
+ \endlist
+
+ The \a creator argument is the widget to use as the dialog's parent.
+ The widget flags, \a f, are passed to the QDialog::QDialog() constructor.
+
+ \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
+ setTotalSteps()
+*/
+Q3ProgressDialog::Q3ProgressDialog(QWidget *creator, Qt::WindowFlags f)
+ : QDialog(creator, f)
+{
+ init(creator, QString::fromLatin1(""), tr("Cancel"), 100);
+}
+
+/*!
+ Constructs a progress dialog.
+
+ The \a labelText is text used to remind the user what is progressing.
+
+ The \a cancelButtonText is the text to display on the cancel button,
+ or 0 if no cancel button is to be shown.
+
+ The \a totalSteps is the total number of steps in the operation for
+ which this progress dialog shows progress. For example, if the
+ operation is to examine 50 files, this value would be 50. Before
+ examining the first file, call setProgress(0). As each file is
+ processed call setProgress(1), setProgress(2), etc., finally
+ calling setProgress(50) after examining the last file.
+
+ The \a creator argument is the widget to use as the dialog's parent.
+ The widget flags, \a f, are passed to the
+ QDialog::QDialog() constructor.
+
+ \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
+ setTotalSteps()
+*/
+Q3ProgressDialog::Q3ProgressDialog(const QString &labelText,
+ const QString &cancelButtonText,
+ int totalSteps, QWidget *creator, Qt::WindowFlags f)
+ : QDialog(creator, f)
+{
+ init(creator, labelText, cancelButtonText, totalSteps);
+}
+
+/*!
+ Destroys the progress dialog.
+*/
+
+Q3ProgressDialog::~Q3ProgressDialog()
+{
+#ifndef QT_NO_CURSOR
+ if (d->creator)
+ d->creator->setCursor(d->parentCursor);
+#endif
+ delete d;
+}
+
+void Q3ProgressDialog::init(QWidget *creator,
+ const QString& lbl, const QString& canc,
+ int totstps)
+{
+ d = new Q3ProgressDialogData(this, creator, lbl, totstps);
+ d->autoClose = true;
+ d->autoReset = true;
+ d->forceHide = false;
+ setCancelButtonText(canc);
+ connect(this, SIGNAL(canceled()), this, SIGNAL(cancelled()));
+ connect(this, SIGNAL(canceled()), this, SLOT(cancel()));
+ forceTimer = new QTimer(this);
+ connect(forceTimer, SIGNAL(timeout()), this, SLOT(forceShow()));
+ layout();
+}
+
+/*!
+ \fn void Q3ProgressDialog::canceled()
+
+ This signal is emitted when the cancel button is clicked.
+ It is connected to the cancel() slot by default.
+
+ \sa wasCanceled()
+*/
+
+/*!
+ \fn void Q3ProgressDialog::cancelled()
+
+ Use canceled() instead.
+*/
+
+/*!
+ Sets the label to \a label. The progress dialog resizes to fit. The
+ label becomes owned by the progress dialog and will be deleted when
+ necessary, so do not pass the address of an object on the stack.
+
+ \sa setLabelText()
+*/
+
+void Q3ProgressDialog::setLabel(QLabel *label)
+{
+ delete d->label;
+ d->label = label;
+ if (label) {
+ if (label->parentWidget() == this) {
+ label->hide(); // until we resize
+ } else {
+ label->setParent(this, 0);
+ }
+ }
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+ if (label)
+ label->show();
+}
+
+
+/*!
+ \property Q3ProgressDialog::labelText
+ \brief the label's text
+
+ The default text is an empty string.
+*/
+
+QString Q3ProgressDialog::labelText() const
+{
+ if (label())
+ return label()->text();
+ return QString();
+}
+
+void Q3ProgressDialog::setLabelText(const QString &text)
+{
+ if (label()) {
+ label()->setText(text);
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+ }
+}
+
+
+/*!
+ Sets the cancel button to the push button, \a cancelButton. The
+ progress dialog takes ownership of this button which will be deleted
+ when necessary, so do not pass the address of an object that is on
+ the stack, i.e. use new() to create the button.
+
+ \sa setCancelButtonText()
+*/
+
+void Q3ProgressDialog::setCancelButton(QPushButton *cancelButton)
+{
+ delete d->cancel;
+ d->cancel = cancelButton;
+ if (cancelButton) {
+ if (cancelButton->parentWidget() == this) {
+ cancelButton->hide(); // until we resize
+ } else {
+ cancelButton->setParent(this, 0);
+ }
+ connect(d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled()));
+ new QShortcut(Qt::Key_Escape, this, SIGNAL(canceled()));
+ }
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+ if (cancelButton)
+ cancelButton->show();
+}
+
+/*!
+ Sets the cancel button's text to \a cancelButtonText.
+ \sa setCancelButton()
+*/
+
+void Q3ProgressDialog::setCancelButtonText(const QString &cancelButtonText)
+{
+ if (!cancelButtonText.isNull()) {
+ if (d->cancel)
+ d->cancel->setText(cancelButtonText);
+ else
+ setCancelButton(new QPushButton(cancelButtonText, this));
+ } else {
+ setCancelButton(0);
+ }
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+}
+
+
+/*!
+ Sets the progress bar widget to \a bar. The progress dialog resizes to
+ fit. The progress dialog takes ownership of the progress \a bar which
+ will be deleted when necessary, so do not use a progress bar
+ allocated on the stack.
+*/
+
+void Q3ProgressDialog::setBar(Q3ProgressBar *bar)
+{
+#ifndef QT_NO_DEBUG
+ if (progress() > 0)
+ qWarning("Q3ProgressDialog::setBar: Cannot set a new progress bar "
+ "while the old one is active");
+#endif
+ delete d->bar;
+ d->bar = bar;
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+}
+
+
+/*!
+ \property Q3ProgressDialog::wasCanceled
+ \brief whether the dialog was canceled
+
+ \sa setProgress()
+*/
+
+bool Q3ProgressDialog::wasCanceled() const
+{
+ return d->cancellation_flag;
+}
+
+/*!
+ \property Q3ProgressDialog::wasCancelled
+ \brief whether the dialog was canceled
+ \since 4.2
+
+ Use \l wasCanceled instead.
+*/
+
+/*!
+ Use wasCanceled() instead.
+*/
+bool Q3ProgressDialog::wasCancelled() const
+{
+ return d->cancellation_flag;
+}
+
+/*!
+ \property Q3ProgressDialog::totalSteps
+ \brief the total number of steps
+
+ The default is 0.
+*/
+
+int Q3ProgressDialog::totalSteps() const
+{
+ if (d && d->bar)
+ return bar()->totalSteps();
+ return 0;
+}
+
+void Q3ProgressDialog::setTotalSteps(int totalSteps)
+{
+ bar()->setTotalSteps(totalSteps);
+}
+
+
+/*!
+ Resets the progress dialog.
+ The progress dialog becomes hidden if autoClose() is true.
+
+ \sa setAutoClose(), setAutoReset()
+*/
+
+void Q3ProgressDialog::reset()
+{
+#ifndef QT_NO_CURSOR
+ if (progress() >= 0) {
+ if (d->creator)
+ d->creator->setCursor(d->parentCursor);
+ }
+#endif
+ if (d->autoClose || d->forceHide)
+ hide();
+ bar()->reset();
+ d->cancellation_flag = false;
+ d->shown_once = false;
+ forceTimer->stop();
+}
+
+/*!
+ Resets the progress dialog. wasCanceled() becomes true until
+ the progress dialog is reset.
+ The progress dialog becomes hidden.
+*/
+
+void Q3ProgressDialog::cancel()
+{
+ d->forceHide = true;
+ reset();
+ d->forceHide = false;
+ d->cancellation_flag = true;
+}
+
+/*!
+ \property Q3ProgressDialog::progress
+ \brief the current amount of progress made.
+
+ For the progress dialog to work as expected, you should initially set
+ this property to 0 and finally set it to
+ Q3ProgressDialog::totalSteps(); you can call setProgress() any number of times
+ in-between.
+
+ \warning If the progress dialog is modal
+ (see Q3ProgressDialog::Q3ProgressDialog()),
+ this function calls QApplication::processEvents(), so take care that
+ this does not cause undesirable re-entrancy in your code. For example,
+ don't use a Q3ProgressDialog inside a paintEvent()!
+
+ \sa totalSteps
+*/
+
+int Q3ProgressDialog::progress() const
+{
+ return bar()->progress();
+}
+
+void Q3ProgressDialog::setProgress(int progress)
+{
+ if (progress == bar()->progress()
+ || (bar()->progress() == -1 && progress == bar()->totalSteps()))
+ return;
+
+ bar()->setProgress(progress);
+
+ if (d->shown_once) {
+ if (isModal())
+ qApp->processEvents();
+ } else {
+ if (progress == 0) {
+#ifndef QT_NO_CURSOR
+ if (d->creator) {
+ d->parentCursor = d->creator->cursor();
+ d->creator->setCursor(Qt::WaitCursor);
+ }
+#endif
+ d->starttime.start();
+ forceTimer->start(d->showTime);
+ return;
+ } else {
+ bool need_show;
+ int elapsed = d->starttime.elapsed();
+ if (elapsed >= d->showTime) {
+ need_show = true;
+ } else {
+ if (elapsed > minWaitTime) {
+ int estimate;
+ if ((totalSteps() - progress) >= INT_MAX / elapsed)
+ estimate = (totalSteps() - progress) / progress * elapsed;
+ else
+ estimate = elapsed * (totalSteps() - progress) / progress;
+ need_show = estimate >= d->showTime;
+ } else {
+ need_show = false;
+ }
+ }
+ if (need_show) {
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+ show();
+ d->shown_once = true;
+ }
+ }
+#ifdef Q_WS_MAC
+ QApplication::flush();
+#endif
+ }
+
+ if (progress == bar()->totalSteps() && d->autoReset)
+ reset();
+}
+
+/*!
+ \overload
+
+ Sets the current amount of progress to \a progress and the total number of
+ steps to \a totalSteps.
+
+ \sa setTotalSteps()
+*/
+
+void Q3ProgressDialog::setProgress(int progress, int totalSteps)
+{
+ setTotalSteps(totalSteps);
+ setProgress(progress);
+}
+
+/*!
+ Returns a size that fits the contents of the progress dialog.
+ The progress dialog resizes itself as required, so you should not
+ need to call this yourself.
+*/
+
+QSize Q3ProgressDialog::sizeHint() const
+{
+ QSize sh = label()->sizeHint();
+ QSize bh = bar()->sizeHint();
+ int h = margin_tb*2 + bh.height() + sh.height() + spacing;
+ if (d->cancel)
+ h += d->cancel->sizeHint().height() + spacing;
+ return QSize(qMax(200, sh.width() + 2*margin_lr), h);
+}
+
+/*!\reimp
+*/
+void Q3ProgressDialog::resizeEvent(QResizeEvent *)
+{
+ layout();
+}
+
+/*!
+ \reimp
+*/
+void Q3ProgressDialog::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange)
+ layout();
+ QDialog::changeEvent(ev);
+}
+
+void Q3ProgressDialog::layout()
+{
+ int sp = spacing;
+ int mtb = margin_tb;
+ int mlr = qMin(width()/10, margin_lr);
+ const bool centered =
+ bool(style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, this));
+
+ QSize cs = d->cancel ? d->cancel->sizeHint() : QSize(0,0);
+ QSize bh = bar()->sizeHint();
+ int cspc;
+ int lh = 0;
+
+ // Find spacing and sizes that fit. It is important that a progress
+ // dialog can be made very small if the user demands it so.
+ for (int attempt=5; attempt--;) {
+ cspc = d->cancel ? cs.height() + sp : 0;
+ lh = qMax(0, height() - mtb - bh.height() - sp - cspc);
+
+ if (lh < height()/4) {
+ // Getting cramped
+ sp /= 2;
+ mtb /= 2;
+ if (d->cancel) {
+ cs.setHeight(qMax(4,cs.height()-sp-2));
+ }
+ bh.setHeight(qMax(4,bh.height()-sp-1));
+ } else {
+ break;
+ }
+ }
+
+ if (d->cancel) {
+ d->cancel->setGeometry(
+ centered ? width()/2 - cs.width()/2 : width() - mlr - cs.width(),
+ height() - mtb - cs.height() + sp,
+ cs.width(), cs.height());
+ }
+
+ label()->setGeometry(mlr, 0, width()-mlr*2, lh);
+ bar()->setGeometry(mlr, lh+sp, width()-mlr*2, bh.height());
+}
+
+/*!
+ \property Q3ProgressDialog::minimumDuration
+ \brief the time that must pass before the dialog appears
+
+ If the expected duration of the task is less than the
+ minimumDuration, the dialog will not appear at all. This prevents
+ the dialog popping up for tasks that are quickly over. For tasks
+ that are expected to exceed the minimumDuration, the dialog will
+ pop up after the minimumDuration time or as soon as any progress
+ is set.
+
+ If set to 0, the dialog is always shown as soon as any progress is
+ set. The default is 4000 milliseconds.
+*/
+void Q3ProgressDialog::setMinimumDuration(int ms)
+{
+ d->showTime = ms;
+ if (bar()->progress() == 0) {
+ forceTimer->stop();
+ forceTimer->start(ms);
+ }
+}
+
+int Q3ProgressDialog::minimumDuration() const
+{
+ return d->showTime;
+}
+
+
+/*!
+ \reimp
+*/
+
+void Q3ProgressDialog::closeEvent(QCloseEvent *e)
+{
+ emit canceled();
+ QDialog::closeEvent(e);
+}
+
+/*!
+ \property Q3ProgressDialog::autoReset
+ \brief whether the progress dialog calls reset() as soon as progress() equals totalSteps()
+
+ The default is true.
+
+ \sa setAutoClose()
+*/
+
+void Q3ProgressDialog::setAutoReset(bool b)
+{
+ d->autoReset = b;
+}
+
+bool Q3ProgressDialog::autoReset() const
+{
+ return d->autoReset;
+}
+
+/*!
+ \property Q3ProgressDialog::autoClose
+ \brief whether the dialog gets hidden by reset()
+
+ The default is true.
+
+ \sa setAutoReset()
+*/
+
+void Q3ProgressDialog::setAutoClose(bool b)
+{
+ d->autoClose = b;
+}
+
+bool Q3ProgressDialog::autoClose() const
+{
+ return d->autoClose;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3ProgressDialog::showEvent(QShowEvent *e)
+{
+ QDialog::showEvent(e);
+ int w = qMax(isVisible() ? width() : 0, sizeHint().width());
+ int h = qMax(isVisible() ? height() : 0, sizeHint().height());
+ resize(w, h);
+ forceTimer->stop();
+}
+
+/*!
+ Shows the dialog if it is still hidden after the algorithm has been started
+ and minimumDuration milliseconds have passed.
+
+ \sa setMinimumDuration()
+*/
+
+void Q3ProgressDialog::forceShow()
+{
+ if (d->shown_once || d->cancellation_flag)
+ return;
+
+ show();
+ d->shown_once = true;
+}
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/dialogs/q3progressdialog.h b/src/qt3support/dialogs/q3progressdialog.h
new file mode 100644
index 0000000..a5934f6
--- /dev/null
+++ b/src/qt3support/dialogs/q3progressdialog.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PROGRESSDIALOG_H
+#define Q3PROGRESSDIALOG_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_PROGRESSDIALOG
+
+class Q3ProgressDialogData;
+class QLabel;
+class QPushButton;
+class QTimer;
+class Q3ProgressBar;
+
+class Q_COMPAT_EXPORT Q3ProgressDialog : public QDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(bool wasCanceled READ wasCanceled)
+ Q_PROPERTY(bool wasCancelled READ wasCancelled DESIGNABLE false STORED false)
+ Q_PROPERTY(int totalSteps READ totalSteps WRITE setTotalSteps)
+ Q_PROPERTY(int progress READ progress WRITE setProgress)
+ Q_PROPERTY(bool autoReset READ autoReset WRITE setAutoReset)
+ Q_PROPERTY(bool autoClose READ autoClose WRITE setAutoClose)
+ Q_PROPERTY(int minimumDuration READ minimumDuration WRITE setMinimumDuration)
+ Q_PROPERTY(QString labelText READ labelText WRITE setLabelText)
+
+public:
+ Q3ProgressDialog(QWidget* parent, const char* name, bool modal=false,
+ Qt::WindowFlags f=0);
+ Q3ProgressDialog(const QString& labelText,
+ const QString &cancelButtonText, int totalSteps,
+ QWidget* parent=0, const char* name=0,
+ bool modal=false, Qt::WindowFlags f=0);
+ Q3ProgressDialog(QWidget* parent = 0, Qt::WindowFlags f = 0);
+ Q3ProgressDialog(const QString& labelText, const QString &cancelButtonText,
+ int totalSteps, QWidget* parent=0, Qt::WindowFlags f=0);
+ ~Q3ProgressDialog();
+
+ void setLabel(QLabel *);
+ void setCancelButton(QPushButton *);
+ void setBar(Q3ProgressBar *);
+
+ bool wasCanceled() const;
+ bool wasCancelled() const;
+
+ int totalSteps() const;
+ int progress() const;
+
+ QSize sizeHint() const;
+
+ QString labelText() const;
+
+ void setAutoReset(bool b);
+ bool autoReset() const;
+ void setAutoClose(bool b);
+ bool autoClose() const;
+
+public Q_SLOTS:
+ void cancel();
+ void reset();
+ void setTotalSteps(int totalSteps);
+ void setProgress(int progress);
+ void setProgress(int progress, int totalSteps);
+ void setLabelText(const QString &);
+ void setCancelButtonText(const QString &);
+
+ void setMinimumDuration(int ms);
+public:
+ int minimumDuration() const;
+
+Q_SIGNALS:
+ void canceled();
+ void cancelled();
+
+protected:
+ void resizeEvent(QResizeEvent *);
+ void closeEvent(QCloseEvent *);
+ void changeEvent(QEvent *);
+ void showEvent(QShowEvent *e);
+
+protected Q_SLOTS:
+ void forceShow();
+
+private:
+ void init(QWidget *creator, const QString& lbl, const QString &canc,
+ int totstps);
+ void layout();
+ QLabel *label() const;
+ Q3ProgressBar *bar() const;
+ Q3ProgressDialogData *d;
+ QTimer *forceTimer;
+
+private:
+ Q_DISABLE_COPY(Q3ProgressDialog)
+};
+
+#endif // QT_NO_PROGRESSDIALOG
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PROGRESSDIALOG_H
diff --git a/src/qt3support/dialogs/q3tabdialog.cpp b/src/qt3support/dialogs/q3tabdialog.cpp
new file mode 100644
index 0000000..ac53c5b
--- /dev/null
+++ b/src/qt3support/dialogs/q3tabdialog.cpp
@@ -0,0 +1,1087 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3tabdialog.h"
+
+#include "qtabbar.h"
+#include "qtabwidget.h"
+#include "qpushbutton.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qapplication.h"
+#include "q3widgetstack.h"
+#include "qlayout.h"
+#include "qevent.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+/*!
+ \class Q3TabDialog
+ \compat
+ \brief The Q3TabDialog class provides a stack of tabbed widgets.
+
+ A tabbed dialog is one in which several "tab pages" are available.
+ By clicking on a tab page's tab or by pressing the indicated
+ Alt+\e{letter} key combination, the user can select which tab page
+ they want to use.
+
+ Q3TabDialog provides a tab bar consisting of single row of tabs at
+ the top; each tab has an associated widget which is that tab's
+ tab page. In addition, Q3TabDialog provides an OK button and the
+ following optional buttons: Apply, Cancel, Defaults and Help.
+
+ The normal way to use Q3TabDialog is to do the following in the
+ constructor:
+ \list 1
+ \i Create a Q3TabDialog.
+ \i Create a QWidget for each of the pages in the tab dialog, insert
+ children into it, set up geometry management for it, and use
+ addTab() (or insertTab()) to set up a tab and keyboard accelerator
+ for it.
+ \i Set up the buttons for the tab dialog using setOkButton(),
+ setApplyButton(), setDefaultsButton(), setCancelButton() and
+ setHelpButton().
+ \i Connect to the signals and slots.
+ \endlist
+
+ If you don't call addTab() the page you have created will not be
+ visible. Don't confuse the object name you supply to the
+ QWidget constructor and the tab label you supply to addTab();
+ addTab() takes user-visible name that appears on the widget's tab
+ and may identify an accelerator, whereas the widget name is used
+ primarily for debugging.
+
+ Almost all applications have to connect the applyButtonPressed()
+ signal to something. applyButtonPressed() is emitted when either OK
+ or Apply is clicked, and your slot must copy the dialog's state into
+ the application.
+
+ There are also several other signals which may be useful:
+ \list
+ \i cancelButtonPressed() is emitted when the user clicks Cancel.
+ \i defaultButtonPressed() is emitted when the user clicks Defaults;
+ the slot it is connected to should reset the state of the dialog to
+ the application defaults.
+ \i helpButtonPressed() is emitted when the user clicks Help.
+ \i aboutToShow() is emitted at the start of show(); if there is any
+ chance that the state of the application may change between the
+ creation of the tab dialog and the time show() is called, you must
+ connect this signal to a slot that resets the state of the dialog.
+ \i currentChanged() is emitted when the user selects a page.
+ \endlist
+
+ Each tab is either enabled or disabled at any given time (see
+ setTabEnabled()). If a tab is enabled the tab text is drawn in
+ black and the user can select that tab. If it is disabled the tab
+ is drawn in a different way and the user cannot select that tab.
+ Note that even if a tab is disabled, the page can still be visible;
+ for example, if all of the tabs happen to be disabled.
+
+ You can change a tab's label and iconset using changeTab(). A tab
+ page can be removed with removePage() and shown with showPage(). The
+ current page is given by currentPage().
+
+ Q3TabDialog does not support tabs on the sides or bottom, nor can
+ you set or retrieve the visible page. If you need more functionality
+ than Q3TabDialog provides, consider creating a QDialog and using a
+ QTabBar with QTabWidgets.
+
+ Most of the functionality in Q3TabDialog is provided by a QTabWidget.
+*/
+
+/*!
+ \fn void Q3TabDialog::selected(const QString &name)
+
+ This signal is emitted whenever a tab is selected (raised),
+ including during the first show(). \a name is the name of the
+ selected tab.
+
+ \sa raise()
+*/
+
+/*! \fn void Q3TabDialog::currentChanged(QWidget *widget)
+
+ This signal is emitted whenever the current page changes. \a widget
+ is the new current page.
+
+ \sa currentPage(), showPage(), tabLabel()
+*/
+
+class Q3TabDialogPrivate
+{
+public:
+ Q3TabDialogPrivate();
+
+ QTabWidget* tw;
+
+ QPushButton * ok;
+ QPushButton * cb;
+ QPushButton * db;
+ QPushButton * hb;
+ QPushButton * ab;
+
+ QBoxLayout * tll;
+};
+
+Q3TabDialogPrivate::Q3TabDialogPrivate()
+ : tw(0),
+ ok(0), cb(0), db(0), hb(0), ab(0),
+ tll(0)
+{ }
+
+/*!
+ Constructs a Q3TabDialog with only an OK button.
+ The \a parent, \a name, \a modal and widget flag, \a f, arguments
+ are passed on to the QDialog constructor.
+*/
+
+Q3TabDialog::Q3TabDialog(QWidget *parent, const char *name, bool modal, Qt::WindowFlags f)
+ : QDialog(parent, name, modal, f)
+{
+ d = new Q3TabDialogPrivate;
+ Q_CHECK_PTR(d);
+
+ d->tw = new QTabWidget(this, "tab widget");
+ connect (d->tw, SIGNAL (selected(QString)), this, SIGNAL(selected(QString)));
+ connect (d->tw, SIGNAL (currentChanged(QWidget*)), this, SIGNAL(currentChanged(QWidget*)));
+
+ d->ok = new QPushButton(this, "ok");
+ Q_CHECK_PTR(d->ok);
+ d->ok->setText(tr("OK"));
+ d->ok->setDefault(true);
+ connect(d->ok, SIGNAL(clicked()),
+ this, SIGNAL(applyButtonPressed()));
+ connect(d->ok, SIGNAL(clicked()),
+ this, SLOT(accept()));
+}
+
+
+/*!
+ Destroys the tab dialog.
+*/
+
+Q3TabDialog::~Q3TabDialog()
+{
+ delete d;
+}
+
+
+/*!
+ Sets the font for the tabs to \a font.
+
+ If the widget is visible, the display is updated with the new font
+ immediately. There may be some geometry changes, depending on the
+ size of the old and new fonts.
+*/
+
+void Q3TabDialog::setFont(const QFont & font)
+{
+ QDialog::setFont(font);
+ setSizes();
+}
+
+
+/*!
+ \fn void Q3TabDialog::applyButtonPressed();
+
+ This signal is emitted when either the Apply or OK button is clicked.
+
+ It should be connected to a slot (or several slots) that change the
+ application's state according to the state of the dialog.
+
+ \sa cancelButtonPressed() defaultButtonPressed() setApplyButton()
+*/
+
+
+/*!
+ Returns true if the tab dialog has a Defaults button; otherwise
+ returns false.
+
+ \sa setDefaultButton() defaultButtonPressed() hasApplyButton()
+ hasCancelButton()
+*/
+
+bool Q3TabDialog::hasDefaultButton() const
+{
+ return d->db != 0;
+}
+
+
+/*!
+ Returns true if the tab dialog has a Help button; otherwise returns
+ false.
+
+ \sa setHelpButton() helpButtonPressed() hasApplyButton()
+ hasCancelButton()
+*/
+
+bool Q3TabDialog::hasHelpButton() const
+{
+ return d->hb != 0;
+}
+
+
+/*!
+ \fn void Q3TabDialog::cancelButtonPressed();
+
+ This signal is emitted when the Cancel button is clicked. It is
+ automatically connected to QDialog::reject(), which will hide the
+ dialog.
+
+ The Cancel button should not change the application's state at all,
+ so you should generally not need to connect it to any slot.
+
+ \sa applyButtonPressed() defaultButtonPressed() setCancelButton()
+*/
+
+
+/*!
+ Returns true if the tab dialog has a Cancel button; otherwise
+ returns false.
+
+ \sa setCancelButton() cancelButtonPressed() hasApplyButton()
+ hasDefaultButton()
+*/
+
+bool Q3TabDialog::hasCancelButton() const
+{
+ return d->cb != 0;
+}
+
+
+/*!
+ \fn void Q3TabDialog::defaultButtonPressed();
+
+ This signal is emitted when the Defaults button is pressed. It
+ should reset the dialog (but not the application) to the "factory
+ defaults".
+
+ The application's state should not be changed until the user clicks
+ Apply or OK.
+
+ \sa applyButtonPressed() cancelButtonPressed() setDefaultButton()
+*/
+
+
+/*!
+ \fn void Q3TabDialog::helpButtonPressed();
+
+ This signal is emitted when the Help button is pressed. It
+ could be used to present information about how to use the dialog.
+
+ \sa applyButtonPressed() cancelButtonPressed() setHelpButton()
+*/
+
+
+/*!
+ Returns true if the tab dialog has an Apply button; otherwise
+ returns false.
+
+ \sa setApplyButton() applyButtonPressed() hasCancelButton()
+ hasDefaultButton()
+*/
+
+bool Q3TabDialog::hasApplyButton() const
+{
+ return d->ab != 0;
+}
+
+
+/*!
+ Returns true if the tab dialog has an OK button; otherwise returns
+ false.
+
+ \sa setOkButton() hasApplyButton() hasCancelButton()
+ hasDefaultButton()
+*/
+
+bool Q3TabDialog::hasOkButton() const
+{
+ return d->ok != 0;
+}
+
+
+/*!
+ \fn void Q3TabDialog::aboutToShow()
+
+ This signal is emitted by show() when it is time to set the state of
+ the dialog's contents. The dialog should reflect the current state
+ of the application when it appears; if there is any possibility that
+ the state of the application may change between the time you call
+ Q3TabDialog() and show(), you should set the
+ dialog's state in a slot and connect this signal to it.
+
+ This applies mainly to Q3TabDialog objects that are kept around
+ hidden, rather than being created, shown, and deleted afterwards.
+
+ \sa applyButtonPressed(), QWidget::show(), cancelButtonPressed()
+*/
+
+
+/*!
+ \internal
+
+ Implemented to delay show()'ing of every page.
+*/
+void Q3TabDialog::show()
+{
+ // Reimplemented in order to delay show()'ing of every page
+ // except the initially visible one, and in order to emit the
+ // aboutToShow() signal.
+ if (window() == this)
+ d->tw->setFocus();
+ emit aboutToShow();
+ setSizes();
+ setUpLayout();
+ QDialog::show();
+}
+
+
+
+/*!
+ Adds another tab and page to the tab view.
+
+ The new page is \a child; the tab's label is \a label.
+ Note the difference between the widget name (which you supply to
+ widget constructors and to setTabEnabled(), for example) and the tab
+ label. The name is internal to the program and invariant, whereas
+ the label is shown on-screen and may vary according to language and
+ other factors.
+
+ If the tab's \a label contains an ampersand, the letter following
+ the ampersand is used as an accelerator for the tab, e.g. if the
+ label is "Bro&wse" then Alt+W becomes an accelerator which will
+ move the focus to this tab.
+
+ If you call addTab() after show() the screen will flicker and the
+ user may be confused.
+
+ \sa insertTab()
+*/
+
+void Q3TabDialog::addTab(QWidget * child, const QString &label)
+{
+ d->tw->addTab(child, label);
+}
+
+
+
+/*! \overload
+
+ This version of the function shows the \a iconset as well as the \a
+ label on the tab of \a child.
+*/
+void Q3TabDialog::addTab(QWidget *child, const QIcon& iconset, const QString &label)
+{
+ d->tw->addTab(child, iconset, label);
+}
+
+
+/*!
+ Inserts another tab and page to the tab view.
+
+ The new page is \a child; the tab's label is \a label.
+ Note the difference between the widget name (which you supply to
+ widget constructors and to setTabEnabled(), for example) and the tab
+ label. The name is internal to the program and invariant, whereas
+ the label is shown on-screen and may vary according to language and
+ other factors.
+
+ If the tab's \a label contains an ampersand, the letter following
+ the ampersand is used as an accelerator for the tab, e.g. if the
+ label is "Bro&wse" then Alt+W becomes an accelerator which will
+ move the focus to this tab.
+
+ If \a index is not specified, the tab is simply added. Otherwise
+ it is inserted at the specified position.
+
+ If you call insertTab() after show(), the screen will flicker and the
+ user may be confused.
+
+ \sa addTab()
+*/
+
+void Q3TabDialog::insertTab(QWidget * child, const QString &label, int index)
+{
+ d->tw->insertTab(child, label, index);
+}
+
+
+/*! \overload
+
+ This version of the function shows the \a iconset as well as the \a
+ label on the tab of \a child.
+ */
+void Q3TabDialog::insertTab(QWidget *child, const QIcon& iconset, const QString &label, int index)
+{
+ d->tw->insertTab(child, iconset, label, index);
+}
+
+/*!
+ Replaces the QTabBar heading the dialog by the given tab bar, \a tb.
+ Note that this must be called \e before any tabs have been added,
+ or the behavior is undefined.
+ \sa tabBar()
+*/
+void Q3TabDialog::setTabBar(QTabBar* tb)
+{
+ if (tb == 0){
+ qWarning("Q3TabDialog::setTabBar() called with null QTabBar pointer");
+ return;
+ }
+
+ d->tw->setTabBar(tb);
+ setUpLayout();
+}
+
+/*!
+ Returns the currently set QTabBar.
+ \sa setTabBar()
+*/
+QTabBar* Q3TabDialog::tabBar() const
+{
+ return d->tw->tabBar();
+}
+
+/*! Ensures that widget \a w is shown. This is mainly useful for accelerators.
+
+ \warning If used carelessly, this function can easily surprise or
+ confuse the user.
+
+ \sa QTabBar::setCurrentTab()
+*/
+
+void Q3TabDialog::showPage(QWidget * w)
+{
+ d->tw->showPage(w);
+}
+
+
+/*! \obsolete
+ Returns true if the page with object name \a name is enabled and
+ false if it is disabled.
+
+ If \a name is 0 or not the name of any of the pages, isTabEnabled()
+ returns false.
+
+ \sa setTabEnabled(), QWidget::isEnabled()
+*/
+
+bool Q3TabDialog::isTabEnabled(const char* name) const
+{
+ if (!name)
+ return false;
+ QObjectList l = this->queryList("QWidget", name, false, true);
+ if (!l.isEmpty()) {
+ for (int i = 0; i < l.size(); ++i) {
+ QObject *o = l.at(i);
+ if (!o->isWidgetType())
+ continue;
+ QWidget *w = static_cast<QWidget *>(o);
+ return d->tw->isTabEnabled(w);
+ }
+ }
+ return false;
+}
+
+
+/*!\obsolete
+
+ Finds the page with object name \a name, enables/disables it
+ according to the value of \a enable and redraws the page's tab
+ appropriately.
+
+ Q3TabDialog uses QWidget::setEnabled() internally, rather than keeping a
+ separate flag.
+
+ Note that even a disabled tab/page may be visible. If the page is
+ already visible Q3TabDialog will not hide it; if all the pages
+ are disabled Q3TabDialog will show one of them.
+
+ The object name is used (rather than the tab label) because the tab
+ text may not be invariant in multi-language applications.
+
+ \sa isTabEnabled(), QWidget::setEnabled()
+*/
+
+void Q3TabDialog::setTabEnabled(const char* name, bool enable)
+{
+ if (!name)
+ return;
+ QObjectList l = this->queryList("QWidget", name, false, true);
+ if (!l.isEmpty()) {
+ for (int i = 0; i < l.size(); ++i) {
+ QObject *o = l.at(i);
+ if(o->isWidgetType())
+ d->tw->setTabEnabled(static_cast<QWidget*>(o), enable);
+ }
+ }
+}
+
+
+/* ### SHOULD THIS BE HERE?
+ Adds an Apply button to the dialog. The button's text is set to \e
+ text (and defaults to "Apply").
+
+ The Apply button should apply the current settings in the dialog box
+ to the application, while keeping the dialog visible.
+
+ When Apply is clicked, the applyButtonPressed() signal is emitted.
+
+ If \a text is an empty string, no button is shown.
+
+ \sa setCancelButton() setDefaultButton() applyButtonPressed()
+*/
+
+
+/*!
+ Returns true if the page \a w is enabled; otherwise returns false.
+
+ \sa setTabEnabled(), QWidget::isEnabled()
+*/
+
+bool Q3TabDialog::isTabEnabled(QWidget* w) const
+{
+ return d->tw->isTabEnabled(w);
+}
+
+/*!
+ If \a enable is true the page \a w is enabled; otherwise \a w is
+ disabled. The page's tab is redrawn appropriately.
+
+ QTabWidget uses QWidget::setEnabled() internally, rather than keeping a
+ separate flag.
+
+ Note that even a disabled tab and tab page may be visible. If the
+ page is already visible QTabWidget will not hide it; if all the
+ pages are disabled QTabWidget will show one of them.
+
+ \sa isTabEnabled(), QWidget::setEnabled()
+*/
+
+void Q3TabDialog::setTabEnabled(QWidget* w, bool enable)
+{
+ d->tw->setTabEnabled(w, enable);
+}
+
+
+/*!
+ Adds an Apply button to the dialog. The button's text is set to \a
+ text.
+
+ The Apply button should apply the current settings in the dialog box
+ to the application while keeping the dialog visible.
+
+ When Apply is clicked, the applyButtonPressed() signal is emitted.
+
+ If \a text is an empty string, no button is shown.
+
+ \sa setCancelButton() setDefaultButton() applyButtonPressed()
+*/
+void Q3TabDialog::setApplyButton(const QString &text)
+{
+ if (text.isEmpty() && d->ab) {
+ delete d->ab;
+ d->ab = 0;
+ setSizes();
+ } else {
+ if (!d->ab) {
+ d->ab = new QPushButton(this, "apply settings");
+ connect(d->ab, SIGNAL(clicked()),
+ this, SIGNAL(applyButtonPressed()));
+ setUpLayout();
+ }
+ d->ab->setText(text);
+ setSizes();
+ //d->ab->show();
+ }
+}
+
+/*!
+ \overload
+
+ Adds an Apply button to the dialog. The button's text is set to
+ a localizable "Apply".
+ */
+void Q3TabDialog::setApplyButton()
+{
+ setApplyButton(tr("Apply"));
+}
+
+
+/*!
+ Adds a Help button to the dialog. The button's text is set to \a
+ text.
+
+ When Help is clicked, the helpButtonPressed() signal is emitted.
+
+ If \a text is an empty string, no button is shown.
+
+ \sa setApplyButton() setCancelButton() helpButtonPressed()
+*/
+
+void Q3TabDialog::setHelpButton(const QString &text)
+{
+ if (text.isEmpty()) {
+ delete d->hb;
+ d->hb = 0;
+ setSizes();
+ } else {
+ if (!d->hb) {
+ d->hb = new QPushButton(this, "give help");
+ connect(d->hb, SIGNAL(clicked()),
+ this, SIGNAL(helpButtonPressed()));
+ setUpLayout();
+ }
+ d->hb->setText(text);
+ setSizes();
+ //d->hb->show();
+ }
+}
+
+
+/*!
+ \overload
+
+ Adds a Help button to the dialog. The button's text is set to
+ a localizable "Help".
+ */
+void Q3TabDialog::setHelpButton()
+{
+ setHelpButton(tr("Help"));
+}
+
+/*!
+ Adds a Defaults button to the dialog. The button's text is set to \a
+ text.
+
+ The Defaults button should set the dialog (but not the application)
+ back to the application defaults.
+
+ When Defaults is clicked, the defaultButtonPressed() signal is emitted.
+
+ If \a text is an empty string, no button is shown.
+
+ \sa setApplyButton() setCancelButton() defaultButtonPressed()
+*/
+
+void Q3TabDialog::setDefaultButton(const QString &text)
+{
+ if (text.isEmpty()) {
+ delete d->db;
+ d->db = 0;
+ setSizes();
+ } else {
+ if (!d->db) {
+ d->db = new QPushButton(this, "back to default");
+ connect(d->db, SIGNAL(clicked()),
+ this, SIGNAL(defaultButtonPressed()));
+ setUpLayout();
+ }
+ d->db->setText(text);
+ setSizes();
+ //d->db->show();
+ }
+}
+
+
+/*!
+ \overload
+
+ Adds a Defaults button to the dialog. The button's text is set to
+ a localizable "Defaults".
+ */
+void Q3TabDialog::setDefaultButton()
+{
+ setDefaultButton(tr("Defaults"));
+}
+
+/*!
+ Adds a Cancel button to the dialog. The button's text is set to \a
+ text.
+
+ The cancel button should always return the application to the state
+ it was in before the tab view popped up, or if the user has clicked
+ Apply, back to the state immediately after the last Apply.
+
+ When Cancel is clicked, the cancelButtonPressed() signal is emitted.
+ The dialog is closed at the same time.
+
+ If \a text is an empty string, no button is shown.
+
+ \sa setApplyButton() setDefaultButton() cancelButtonPressed()
+*/
+
+void Q3TabDialog::setCancelButton(const QString &text)
+{
+ if (text.isEmpty()) {
+ delete d->cb;
+ d->cb = 0;
+ setSizes();
+ } else {
+ if (!d->cb) {
+ d->cb = new QPushButton(this, "cancel dialog");
+ connect(d->cb, SIGNAL(clicked()),
+ this, SIGNAL(cancelButtonPressed()));
+ connect(d->cb, SIGNAL(clicked()),
+ this, SLOT(reject()));
+ setUpLayout();
+ }
+ d->cb->setText(text);
+ setSizes();
+ //d->cb->show();
+ }
+}
+
+
+/*!
+ \overload
+
+ Adds a Cancel button to the dialog. The button's text is set to
+ a localizable "Cancel".
+ */
+
+void Q3TabDialog::setCancelButton()
+{
+ setCancelButton(tr("Cancel"));
+}
+
+/*! Sets up the layout manager for the tab dialog.
+
+ \sa setSizes() setApplyButton() setCancelButton() setDefaultButton()
+*/
+
+void Q3TabDialog::setUpLayout()
+{
+ // the next four are probably the same, really?
+ const int topMargin = 6;
+ const int leftMargin = 6;
+ const int rightMargin = 6;
+ const int bottomMargin = 6;
+ const int betweenButtonsMargin = 7;
+ const int aboveButtonsMargin = 8;
+
+ delete d->tll;
+ d->tll = new QBoxLayout(this, QBoxLayout::Down);
+
+ // top margin
+ d->tll->addSpacing(topMargin);
+
+ QBoxLayout * tmp = new QHBoxLayout();
+ d->tll->addLayout(tmp, 1);
+ tmp->addSpacing(leftMargin);
+ tmp->addWidget(d->tw, 1);
+ tmp->addSpacing(rightMargin + 2);
+
+ d->tll->addSpacing(aboveButtonsMargin + 2);
+ QBoxLayout * buttonRow = new QBoxLayout(QBoxLayout::RightToLeft);
+ d->tll->addLayout(buttonRow, 0);
+ d->tll->addSpacing(bottomMargin);
+
+ buttonRow->addSpacing(rightMargin);
+ if (d->cb) {
+ buttonRow->addWidget(d->cb, 0);
+ buttonRow->addSpacing(betweenButtonsMargin);
+ d->cb->raise();
+ }
+
+ if (d->ab) {
+ buttonRow->addWidget(d->ab, 0);
+ buttonRow->addSpacing(betweenButtonsMargin);
+ d->ab->raise();
+ }
+
+ if (d->db) {
+ buttonRow->addWidget(d->db, 0);
+ buttonRow->addSpacing(betweenButtonsMargin);
+ d->db->raise();
+ }
+
+ if (d->hb) {
+ buttonRow->addWidget(d->hb, 0);
+ buttonRow->addSpacing(betweenButtonsMargin);
+ d->hb->raise();
+ }
+
+ if (d->ok) {
+ buttonRow->addWidget(d->ok, 0);
+ buttonRow->addSpacing(betweenButtonsMargin);
+ d->ok->raise();
+ }
+
+ // add one custom widget here
+ buttonRow->addStretch(1);
+ // add another custom widget here
+
+ d->tll->activate();
+}
+
+
+/*! Sets up the minimum and maximum sizes for each child widget.
+
+ \sa setUpLayout() setFont()
+*/
+
+void Q3TabDialog::setSizes()
+{
+ // compute largest button size
+ QSize s(0, 0);
+ int bw = s.width();
+ int bh = s.height();
+
+ if (d->ok) {
+ s = d->ok->sizeHint();
+ if (s.width() > bw)
+ bw = s.width();
+ if (s.height() > bh)
+ bh = s.height();
+ }
+
+ if (d->ab) {
+ s = d->ab->sizeHint();
+ if (s.width() > bw)
+ bw = s.width();
+ if (s.height() > bh)
+ bh = s.height();
+ }
+
+ if (d->db) {
+ s = d->db->sizeHint();
+ if (s.width() > bw)
+ bw = s.width();
+ if (s.height() > bh)
+ bh = s.height();
+ }
+
+ if (d->hb) {
+ s = d->hb->sizeHint();
+ if (s.width() > bw)
+ bw = s.width();
+ if (s.height() > bh)
+ bh = s.height();
+ }
+
+ if (d->cb) {
+ s = d->cb->sizeHint();
+ if (s.width() > bw)
+ bw = s.width();
+ if (s.height() > bh)
+ bh = s.height();
+ }
+
+ // and set all the buttons to that size
+ if (d->ok)
+ d->ok->setFixedSize(bw, bh);
+ if (d->ab)
+ d->ab->setFixedSize(bw, bh);
+ if (d->db)
+ d->db->setFixedSize(bw, bh);
+ if (d->hb)
+ d->hb->setFixedSize(bw, bh);
+ if (d->cb)
+ d->cb->setFixedSize(bw, bh);
+
+ // fiddle the tab chain so the buttons are in their natural order
+ QWidget * w = d->ok;
+
+ if (d->hb) {
+ if (w)
+ setTabOrder(w, d->hb);
+ w = d->hb;
+ }
+ if (d->db) {
+ if (w)
+ setTabOrder(w, d->db);
+ w = d->db;
+ }
+ if (d->ab) {
+ if (w)
+ setTabOrder(w, d->ab);
+ w = d->ab;
+ }
+ if (d->cb) {
+ if (w)
+ setTabOrder(w, d->cb);
+ w = d->cb;
+ }
+ setTabOrder(w, d->tw);
+}
+
+/*!\reimp
+*/
+void Q3TabDialog::resizeEvent(QResizeEvent * e)
+{
+ QDialog::resizeEvent(e);
+}
+
+
+/*!\reimp
+*/
+void Q3TabDialog::paintEvent(QPaintEvent *)
+{
+}
+
+
+/*!\reimp
+*/
+void Q3TabDialog::showEvent(QShowEvent *e)
+{
+ if (!e->spontaneous())
+ show();
+ QDialog::showEvent(e);
+}
+
+
+/*!
+ Adds an OK button to the dialog and sets the button's text to \a text.
+
+ When the OK button is clicked, the applyButtonPressed() signal is emitted,
+ and the current settings in the dialog box should be applied to
+ the application. The dialog then closes.
+
+ If \a text is an empty string, no button is shown.
+
+ \sa setCancelButton() setDefaultButton() applyButtonPressed()
+*/
+
+void Q3TabDialog::setOkButton(const QString &text)
+{
+ if (text.isEmpty()) {
+ delete d->ok;
+ d->ok = 0;
+ setSizes();
+ } else {
+ if (!d->ok) {
+ d->ok = new QPushButton(this, "ok");
+ connect(d->ok, SIGNAL(clicked()),
+ this, SIGNAL(applyButtonPressed()));
+ setUpLayout();
+ }
+ d->ok->setText(text);
+ setSizes();
+ //d->ok->show();
+ }
+}
+/*!
+ \overload
+
+ Adds an OK button to the dialog. The button's text is set to
+ a localizable "OK".
+ */
+
+void Q3TabDialog::setOkButton()
+{
+ setOkButton(tr("OK"));
+}
+
+
+/*
+ \overload
+ Old version of setOkButton(), provided for backward compatibility.
+*/
+void Q3TabDialog::setOKButton(const QString &text)
+{
+ // Ugly workaround for original "OK" default argument
+ QString newText(text);
+ if (text.isNull())
+ newText = QString::fromLatin1("OK");
+ setOkButton(newText);
+}
+
+
+/*! Returns the text in the tab for page \a w.
+*/
+
+QString Q3TabDialog::tabLabel(QWidget * w)
+{
+ return d->tw->tabLabel(w);
+}
+
+
+/*! \internal
+*/
+void Q3TabDialog::styleChange(QStyle& s)
+{
+ QDialog::styleChange(s);
+ setSizes();
+}
+
+
+/*! Returns a pointer to the page currently being displayed by the
+tab dialog. The tab dialog does its best to make sure that this value
+is never 0 (but if you try hard enough, it can be).
+*/
+
+QWidget * Q3TabDialog::currentPage() const
+{
+ return d->tw->currentPage();
+}
+
+/*!
+ \overload
+ Defines a new \a label for the tab of page \a w
+ */
+void Q3TabDialog::changeTab(QWidget *w, const QString &label)
+{
+ d->tw->changeTab(w, label);
+}
+
+/*!
+ Changes tab page \a w's iconset to \a iconset and label to \a label.
+
+ */
+void Q3TabDialog::changeTab(QWidget *w, const QIcon& iconset, const QString &label)
+{
+ d->tw->changeTab(w, iconset, label);
+}
+
+/*! Removes page \a w from this stack of widgets. Does not
+ delete \a w.
+ \sa showPage(), QTabWidget::removePage()
+*/
+void Q3TabDialog::removePage(QWidget * w)
+{
+ d->tw->removePage(w);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/dialogs/q3tabdialog.h b/src/qt3support/dialogs/q3tabdialog.h
new file mode 100644
index 0000000..dc5a2e0
--- /dev/null
+++ b/src/qt3support/dialogs/q3tabdialog.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TABDIALOG_H
+#define Q3TABDIALOG_H
+
+#include <QtGui/qdialog.h>
+#include <QtGui/qicon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QTabBar;
+class QTab;
+class Q3TabDialogPrivate;
+
+class Q_COMPAT_EXPORT Q3TabDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ Q3TabDialog(QWidget* parent=0, const char* name=0, bool modal=false, Qt::WindowFlags f=0);
+ ~Q3TabDialog();
+
+ void show();
+ void setFont(const QFont & font);
+
+ void addTab(QWidget *, const QString &);
+ void addTab(QWidget *child, const QIcon& iconset, const QString &label);
+
+ void insertTab(QWidget *, const QString &, int index = -1);
+ void insertTab(QWidget *child, const QIcon& iconset, const QString &label, int index = -1);
+
+ void changeTab(QWidget *, const QString &);
+ void changeTab(QWidget *child, const QIcon& iconset, const QString &label);
+
+ bool isTabEnabled( QWidget *) const;
+ void setTabEnabled(QWidget *, bool);
+ bool isTabEnabled(const char*) const; // compatibility
+ void setTabEnabled(const char*, bool); // compatibility
+
+ void showPage(QWidget *);
+ void removePage(QWidget *);
+ QString tabLabel(QWidget *);
+
+ QWidget * currentPage() const;
+
+ void setDefaultButton(const QString &text);
+ void setDefaultButton();
+ bool hasDefaultButton() const;
+
+ void setHelpButton(const QString &text);
+ void setHelpButton();
+ bool hasHelpButton() const;
+
+ void setCancelButton(const QString &text);
+ void setCancelButton();
+ bool hasCancelButton() const;
+
+ void setApplyButton(const QString &text);
+ void setApplyButton();
+ bool hasApplyButton() const;
+
+#ifndef qdoc
+ void setOKButton(const QString &text = QString());
+#endif
+ void setOkButton(const QString &text);
+ void setOkButton();
+ bool hasOkButton() const;
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void resizeEvent(QResizeEvent *);
+ void showEvent(QShowEvent *);
+ void styleChange(QStyle&);
+ void setTabBar(QTabBar*);
+ QTabBar* tabBar() const;
+
+Q_SIGNALS:
+ void aboutToShow();
+
+ void applyButtonPressed();
+ void cancelButtonPressed();
+ void defaultButtonPressed();
+ void helpButtonPressed();
+
+ void currentChanged(QWidget *);
+ void selected(const QString&); // obsolete
+
+private:
+ void setSizes();
+ void setUpLayout();
+
+ Q3TabDialogPrivate *d;
+
+ Q_DISABLE_COPY(Q3TabDialog)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TABDIALOG_H
diff --git a/src/qt3support/dialogs/q3wizard.cpp b/src/qt3support/dialogs/q3wizard.cpp
new file mode 100644
index 0000000..e958296
--- /dev/null
+++ b/src/qt3support/dialogs/q3wizard.cpp
@@ -0,0 +1,906 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3wizard.h"
+
+#include "qlayout.h"
+#include "qpushbutton.h"
+#include "qcursor.h"
+#include "qlabel.h"
+#include "qapplication.h"
+#include "qlist.h"
+#include "qpainter.h"
+#include "q3accel.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+/*!
+ \class Q3Wizard
+ \compat
+ \brief The Q3Wizard class provides a framework for wizard dialogs.
+
+ A wizard is a special type of input dialog that consists of a
+ sequence of dialog pages. A wizard's purpose is to walk the user
+ through a process step by step. Wizards are useful for complex or
+ infrequently occurring tasks that people may find difficult to
+ learn or do.
+
+ Q3Wizard provides page titles and displays Next, Back, Finish,
+ Cancel, and Help push buttons, as appropriate to the current
+ position in the page sequence. These buttons can be
+ enabled/disabled using setBackEnabled(), setNextEnabled(),
+ setFinishEnabled() and setHelpEnabled().
+
+ Create and populate dialog pages that inherit from QWidget and add
+ them to the wizard using addPage(). Use insertPage() to add a
+ dialog page at a certain position in the page sequence. Use
+ removePage() to remove a page from the page sequence.
+
+ Use currentPage() to retrieve a pointer to the currently displayed
+ page. page() returns a pointer to the page at a certain position
+ in the page sequence.
+
+ Use pageCount() to retrieve the total number of pages in the page
+ sequence. indexOf() will return the index of a page in the page
+ sequence.
+
+ Q3Wizard provides functionality to mark pages as appropriate (or
+ not) in the current context with setAppropriate(). The idea is
+ that a page may be irrelevant and should be skipped depending on
+ the data entered by the user on a preceding page.
+
+ It is generally considered good design to provide a greater number
+ of simple pages with fewer choices rather than a smaller number of
+ complex pages.
+*/
+
+
+class Q3WizardPrivate
+{
+public:
+
+ virtual ~Q3WizardPrivate()
+ {
+ foreach(Page *page, pages)
+ delete page;
+ }
+
+ struct Page {
+ Page( QWidget * widget, const QString & title ):
+ w( widget ), t( title ),
+ backEnabled( true ), nextEnabled( true ), finishEnabled( false ),
+ helpEnabled( true ),
+ appropriate( true )
+ {}
+ QWidget * w;
+ QString t;
+ bool backEnabled;
+ bool nextEnabled;
+ bool finishEnabled;
+ bool helpEnabled;
+ bool appropriate;
+ };
+
+ QVBoxLayout * v;
+ Page * current;
+ QList<Page *> pages;
+ QLabel * title;
+ QPushButton * backButton;
+ QPushButton * nextButton;
+ QPushButton * finishButton;
+ QPushButton * cancelButton;
+ QPushButton * helpButton;
+
+ QFrame * hbar1, * hbar2;
+
+#ifndef QT_NO_ACCEL
+ Q3Accel * accel;
+ int backAccel;
+ int nextAccel;
+#endif
+
+ Page * page( const QWidget * w )
+ {
+ if ( !w )
+ return 0;
+ int i = pages.count();
+ while( --i >= 0 && pages.at( i ) && pages.at( i )->w != w ) { }
+ return i >= 0 ? pages.at( i ) : 0;
+ }
+
+};
+
+
+/*!
+ Constructs an empty wizard dialog. The \a parent, \a name, \a
+ modal and \a f arguments are passed to the QDialog constructor.
+*/
+
+Q3Wizard::Q3Wizard(QWidget *parent, const char *name, bool modal, Qt::WindowFlags f )
+ : QDialog( parent, name, modal, f )
+{
+ d = new Q3WizardPrivate();
+ d->current = 0; // not quite true, but...
+ d->title = new QLabel( this, "title label" );
+
+ // create in nice tab order
+ d->nextButton = new QPushButton( this, "next" );
+ d->finishButton = new QPushButton( this, "finish" );
+ d->helpButton = new QPushButton( this, "help" );
+ d->backButton = new QPushButton( this, "back" );
+ d->cancelButton = new QPushButton( this, "cancel" );
+
+ d->v = 0;
+ d->hbar1 = 0;
+ d->hbar2 = 0;
+
+ d->cancelButton->setText( tr( "&Cancel" ) );
+ d->backButton->setText( tr( "< &Back" ) );
+ d->nextButton->setText( tr( "&Next >" ) );
+ d->finishButton->setText( tr( "&Finish" ) );
+ d->helpButton->setText( tr( "&Help" ) );
+
+ d->nextButton->setDefault( true );
+
+ connect( d->backButton, SIGNAL(clicked()),
+ this, SLOT(back()) );
+ connect( d->nextButton, SIGNAL(clicked()),
+ this, SLOT(next()) );
+ connect( d->finishButton, SIGNAL(clicked()),
+ this, SLOT(accept()) );
+ connect( d->cancelButton, SIGNAL(clicked()),
+ this, SLOT(reject()) );
+ connect( d->helpButton, SIGNAL(clicked()),
+ this, SLOT(help()) );
+
+#ifndef QT_NO_ACCEL
+ d->accel = new Q3Accel( this, "arrow-key accel" );
+ d->backAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Left );
+ d->accel->connectItem( d->backAccel, this, SLOT(back()) );
+ d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right );
+ d->accel->connectItem( d->nextAccel, this, SLOT(next()) );
+#endif
+}
+
+
+/*!
+ Destroys the object and frees any allocated resources, including
+ all pages and controllers.
+*/
+
+Q3Wizard::~Q3Wizard()
+{
+ delete d;
+}
+
+
+/*!
+ \internal
+*/
+
+void Q3Wizard::setVisible(bool show)
+{
+ if ( show && !d->current ) {
+ // No page yet
+ if ( pageCount() > 0 )
+ showPage( d->pages.at( 0 )->w );
+ else
+ showPage( 0 );
+ }
+
+ QDialog::setVisible(show);
+}
+
+
+/*!
+ \internal
+*/
+
+void Q3Wizard::setFont( const QFont & font )
+{
+ QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) );
+ QDialog::setFont( font );
+}
+
+/*!
+ Adds \a page to the end of the page sequence, with the title, \a
+ title.
+*/
+
+void Q3Wizard::addPage( QWidget * page, const QString & title )
+{
+ if ( !page )
+ return;
+ if ( d->page( page ) ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "Q3Wizard::addPage(): already added %s/%s to %s/%s",
+ page->className(), page->name(),
+ className(), name() );
+#endif
+ return;
+ }
+ int i = d->pages.count();
+
+ if( i > 0 )
+ d->pages.at( i - 1 )->nextEnabled = true;
+
+ Q3WizardPrivate::Page * p = new Q3WizardPrivate::Page( page, title );
+ p->backEnabled = ( i > 0 );
+ d->pages.append( p );
+}
+
+/*!
+ Inserts \a page at position \a index into the page sequence, with
+ title \a title. If \a index is -1, the page will be appended to
+ the end of the wizard's page sequence.
+*/
+
+void Q3Wizard::insertPage( QWidget * page, const QString & title, int index )
+{
+ if ( !page )
+ return;
+ if ( d->page( page ) ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "Q3Wizard::insertPage(): already added %s/%s to %s/%s",
+ page->className(), page->name(),
+ className(), name() );
+#endif
+ return;
+ }
+
+ if ( index < 0 || index > (int)d->pages.count() )
+ index = d->pages.count();
+
+ if( index > 0 && ( index == (int)d->pages.count() ) )
+ d->pages.at( index - 1 )->nextEnabled = true;
+
+ Q3WizardPrivate::Page * p = new Q3WizardPrivate::Page( page, title );
+ p->backEnabled = ( index > 0 );
+ p->nextEnabled = ( index < (int)d->pages.count() );
+
+ d->pages.insert( index, p );
+}
+
+/*!
+ \fn void Q3Wizard::selected(const QString &title)
+
+ This signal is emitted when the current page changes. The
+ \a title parameter contains the title of the selected page.
+*/
+
+
+/*!
+ Makes \a page the current page and emits the selected() signal.
+
+ This virtual function is called whenever a different page is to
+ be shown, including the first time the Q3Wizard is shown.
+ By reimplementing it (and calling Q3Wizard::showPage()),
+ you can prepare each page prior to it being shown.
+*/
+
+void Q3Wizard::showPage( QWidget * page )
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( p ) {
+ int i;
+ for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ ) {}
+ bool notFirst( false );
+
+ if( i ) {
+ i--;
+ while( ( i >= 0 ) && !notFirst ) {
+ notFirst |= appropriate( d->pages.at( i )->w );
+ i--;
+ }
+ }
+ setBackEnabled( notFirst );
+ setNextEnabled( true );
+
+ page->show();
+ foreach(Q3WizardPrivate::Page *ppage, d->pages) {
+ if (ppage->w != page)
+ ppage->w->hide();
+ }
+
+ d->current = p;
+ }
+
+ layOut();
+ updateButtons();
+ emit selected( p ? p->t : QString() );
+}
+
+
+/*!
+ Returns the number of pages in the wizard.
+*/
+
+int Q3Wizard::pageCount() const
+{
+ return d->pages.count();
+}
+
+/*!
+ Returns the position of page \a page. If the page is not part of
+ the wizard -1 is returned.
+*/
+
+int Q3Wizard::indexOf( QWidget* page ) const
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( !p ) return -1;
+
+ return d->pages.indexOf( p );
+}
+
+/*!
+ Called when the user clicks the Back button; this function shows
+ the preceding relevant page in the sequence.
+
+ \sa appropriate()
+*/
+void Q3Wizard::back()
+{
+ int i = 0;
+
+ while( i < (int)d->pages.count() && d->pages.at( i ) &&
+ d->current && d->pages.at( i )->w != d->current->w )
+ i++;
+
+ i--;
+ while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
+ i--;
+
+ if( i >= 0 )
+ if( d->pages.at( i ) )
+ showPage( d->pages.at( i )->w );
+}
+
+
+/*!
+ Called when the user clicks the Next button, this function shows
+ the next relevant page in the sequence.
+
+ \sa appropriate()
+*/
+void Q3Wizard::next()
+{
+ int i = 0;
+ while( i < (int)d->pages.count() && d->pages.at( i ) &&
+ d->current && d->pages.at( i )->w != d->current->w )
+ i++;
+ i++;
+ while( i <= (int)d->pages.count()-1 &&
+ ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
+ i++;
+ // if we fell of the end of the world, step back
+ while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
+ i--;
+ if ( d->pages.at( i ) )
+ showPage( d->pages.at( i )->w );
+}
+
+
+/*!
+ \fn void Q3Wizard::helpClicked()
+
+ This signal is emitted when the user clicks on the Help button.
+*/
+
+/*!
+ Called when the user clicks the Help button, this function emits
+ the helpClicked() signal.
+*/
+
+void Q3Wizard::help()
+{
+ QWidget *page = d->current ? d->current->w : 0;
+ if ( !page )
+ return;
+
+#if 0
+ Q3WizardPage *wpage = qobject_cast<Q3WizardPage*>(page);
+ if ( wpage )
+ emit wpage->helpClicked();
+#endif
+ emit helpClicked();
+}
+
+
+void Q3Wizard::setBackEnabled( bool enable )
+{
+ d->backButton->setEnabled( enable );
+#ifndef QT_NO_ACCEL
+ d->accel->setItemEnabled( d->backAccel, enable );
+#endif
+}
+
+
+void Q3Wizard::setNextEnabled( bool enable )
+{
+ d->nextButton->setEnabled( enable );
+#ifndef QT_NO_ACCEL
+ d->accel->setItemEnabled( d->nextAccel, enable );
+#endif
+}
+
+
+void Q3Wizard::setHelpEnabled( bool enable )
+{
+ d->helpButton->setEnabled( enable );
+}
+
+
+/*!
+ \fn void Q3Wizard::setFinish(QWidget *widget, bool finish)
+ \obsolete
+
+ Use setFinishEnabled() instead.
+*/
+
+/*!
+ If \a enable is true, page \a page has a Back button; otherwise \a
+ page has no Back button. By default all pages have this button.
+*/
+void Q3Wizard::setBackEnabled( QWidget * page, bool enable )
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( !p )
+ return;
+
+ p->backEnabled = enable;
+ updateButtons();
+}
+
+
+/*!
+ If \a enable is true, page \a page has a Next button; otherwise
+ the Next button on \a page is disabled. By default all pages have
+ this button.
+*/
+
+void Q3Wizard::setNextEnabled( QWidget * page, bool enable )
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( !p )
+ return;
+
+ p->nextEnabled = enable;
+ updateButtons();
+}
+
+
+/*!
+ If \a enable is true, page \a page has a Finish button; otherwise
+ \a page has no Finish button. By default \e no page has this
+ button.
+*/
+void Q3Wizard::setFinishEnabled( QWidget * page, bool enable )
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( !p )
+ return;
+
+ p->finishEnabled = enable;
+ updateButtons();
+}
+
+
+/*!
+ If \a enable is true, page \a page has a Help button; otherwise \a
+ page has no Help button. By default all pages have this button.
+*/
+void Q3Wizard::setHelpEnabled( QWidget * page, bool enable )
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( !p )
+ return;
+
+ p->helpEnabled = enable;
+ updateButtons();
+}
+
+
+/*!
+ Called when the Next button is clicked; this virtual function
+ returns true if \a page is relevant for display in the current
+ context; otherwise it is ignored by Q3Wizard and returns false. The
+ default implementation returns the value set using
+ setAppropriate(). The ultimate default is true.
+
+ \warning The last page of the wizard will be displayed if no page
+ is relevant in the current context.
+*/
+
+bool Q3Wizard::appropriate( QWidget * page ) const
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ return p ? p->appropriate : true;
+}
+
+
+/*!
+ If \a appropriate is true then page \a page is considered relevant
+ in the current context and should be displayed in the page
+ sequence; otherwise \a page should not be displayed in the page
+ sequence.
+
+ \sa appropriate()
+*/
+void Q3Wizard::setAppropriate( QWidget * page, bool appropriate )
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( p )
+ p->appropriate = appropriate;
+}
+
+
+void Q3Wizard::updateButtons()
+{
+ if ( !d->current )
+ return;
+
+ int i;
+ for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ ) {}
+ bool notFirst( false );
+ if( i ) {
+ i--;
+ while( ( i >= 0 ) && !notFirst ) {
+ notFirst |= appropriate( d->pages.at( i )->w );
+ i--;
+ }
+ }
+ setBackEnabled( d->current->backEnabled && notFirst );
+ setNextEnabled( d->current->nextEnabled );
+ d->finishButton->setEnabled( d->current->finishEnabled );
+ d->helpButton->setEnabled( d->current->helpEnabled );
+
+ if ( ( d->current->finishEnabled && !d->finishButton->isVisible() ) ||
+ ( d->current->backEnabled && !d->backButton->isVisible() ) ||
+ ( d->current->nextEnabled && !d->nextButton->isVisible() ) ||
+ ( d->current->helpEnabled && !d->helpButton->isVisible() ) )
+ layOut();
+}
+
+
+/*!
+ Returns a pointer to the current page in the sequence. Although
+ the wizard does its best to make sure that this value is never 0,
+ it can be if you try hard enough.
+*/
+
+QWidget * Q3Wizard::currentPage() const
+{
+ return d->current ? d->current->w : 0;
+}
+
+
+/*!
+ Returns the title of page \a page.
+*/
+
+QString Q3Wizard::title( QWidget * page ) const
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ return p ? p->t : QString();
+}
+
+/*!
+ Sets the title for page \a page to \a title.
+*/
+
+void Q3Wizard::setTitle( QWidget *page, const QString &title )
+{
+ Q3WizardPrivate::Page * p = d->page( page );
+ if ( p )
+ p->t = title;
+ if ( page == currentPage() )
+ d->title->setText( title );
+}
+
+/*!
+ \property Q3Wizard::titleFont
+ \brief the font used for page titles
+
+ The default is QApplication::font().
+*/
+QFont Q3Wizard::titleFont() const
+{
+ return d->title->font();
+}
+
+void Q3Wizard::setTitleFont( const QFont & font )
+{
+ d->title->setFont( font );
+}
+
+
+/*!
+ Returns a pointer to the dialog's Back button
+
+ By default, this button is connected to the back() slot, which is
+ virtual so you can reimplement it in a Q3Wizard subclass. Use
+ setBackEnabled() to enable/disable this button.
+*/
+QPushButton * Q3Wizard::backButton() const
+{
+ return d->backButton;
+}
+
+
+/*!
+ Returns a pointer to the dialog's Next button
+
+ By default, this button is connected to the next() slot, which is
+ virtual so you can reimplement it in a Q3Wizard subclass. Use
+ setNextEnabled() to enable/disable this button.
+*/
+QPushButton * Q3Wizard::nextButton() const
+{
+ return d->nextButton;
+}
+
+
+/*!
+ Returns a pointer to the dialog's Finish button
+
+ By default, this button is connected to the QDialog::accept()
+ slot, which is virtual so you can reimplement it in a Q3Wizard
+ subclass. Use setFinishEnabled() to enable/disable this button.
+*/
+QPushButton * Q3Wizard::finishButton() const
+{
+ return d->finishButton;
+}
+
+
+/*!
+ Returns a pointer to the dialog's Cancel button
+
+ By default, this button is connected to the QDialog::reject()
+ slot, which is virtual so you can reimplement it in a Q3Wizard
+ subclass.
+*/
+QPushButton * Q3Wizard::cancelButton() const
+{
+ return d->cancelButton;
+}
+
+
+/*!
+ Returns a pointer to the dialog's Help button
+
+ By default, this button is connected to the help() slot, which is
+ virtual so you can reimplement it in a Q3Wizard subclass. Use
+ setHelpEnabled() to enable/disable this button.
+*/
+QPushButton * Q3Wizard::helpButton() const
+{
+ return d->helpButton;
+}
+
+
+/*!
+ This virtual function is responsible for adding the buttons below
+ the bottom divider.
+
+ \a layout is the horizontal layout of the entire wizard.
+*/
+
+void Q3Wizard::layOutButtonRow( QHBoxLayout * layout )
+{
+ bool hasHelp = false;
+ bool hasEarlyFinish = false;
+
+ int i = d->pages.count() - 2;
+ while ( !hasEarlyFinish && i >= 0 ) {
+ if ( d->pages.at( i ) && d->pages.at( i )->finishEnabled )
+ hasEarlyFinish = true;
+ i--;
+ }
+ i = 0;
+ while ( !hasHelp && i < (int)d->pages.count() ) {
+ if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled )
+ hasHelp = true;
+ i++;
+ }
+
+ QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight );
+ layout->addLayout( h );
+
+ if ( hasHelp )
+ h->addWidget( d->helpButton );
+ else
+ d->helpButton->hide();
+
+ h->addStretch( 42 );
+
+ h->addWidget( d->backButton );
+
+ h->addSpacing( 6 );
+
+ if (d->current == d->pages.at( d->pages.count()-1 ))
+ hasEarlyFinish = false;
+
+ if ( hasEarlyFinish ) {
+ d->nextButton->show();
+ d->finishButton->show();
+ h->addWidget( d->nextButton );
+ h->addSpacing( 12 );
+ h->addWidget( d->finishButton );
+ } else if ( d->pages.count() == 0 ||
+ (d->current && d->current->finishEnabled) ||
+ d->current == d->pages.at( d->pages.count()-1 ) ) {
+ d->nextButton->hide();
+ d->finishButton->show();
+ h->addWidget( d->finishButton );
+ } else {
+ d->nextButton->show();
+ d->finishButton->hide();
+ h->addWidget( d->nextButton );
+ }
+
+ // if last page is disabled - show finished btn. at lastpage-1
+ i = d->pages.count()-1;
+ if ( i >= 0 && !appropriate( d->pages.at( i )->w ) &&
+ d->current == d->pages.at( d->pages.count()-2 ) ) {
+ d->nextButton->hide();
+ d->finishButton->show();
+ h->addWidget( d->finishButton );
+ }
+
+ h->addSpacing( 12 );
+ h->addWidget( d->cancelButton );
+}
+
+
+/*!
+ This virtual function is responsible for laying out the title row.
+
+ \a layout is the horizontal layout for the wizard, and \a
+ title is the title for this page. This function is called every
+ time \a title changes.
+*/
+
+void Q3Wizard::layOutTitleRow( QHBoxLayout * layout, const QString & title )
+{
+ d->title->setText( title );
+ layout->addWidget( d->title, 10 );
+}
+
+
+/*
+
+*/
+
+void Q3Wizard::layOut()
+{
+ delete d->v;
+ d->v = new QVBoxLayout( this, 6, 0, "top-level layout" );
+
+ QHBoxLayout * l;
+ l = new QHBoxLayout( 6 );
+ d->v->addLayout( l, 0 );
+ layOutTitleRow( l, d->current ? d->current->t : QString() );
+
+ if ( ! d->hbar1 ) {
+ d->hbar1 = new QFrame( this, "<hr>", 0 );
+ d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine );
+ d->hbar1->setFixedHeight( 12 );
+ }
+
+ d->v->addWidget( d->hbar1 );
+
+ if (d->current)
+ d->v->addWidget(d->current->w, 10);
+
+ if ( ! d->hbar2 ) {
+ d->hbar2 = new QFrame( this, "<hr>", 0 );
+ d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine );
+ d->hbar2->setFixedHeight( 12 );
+ }
+ d->v->addWidget( d->hbar2 );
+
+ l = new QHBoxLayout( 6 );
+ d->v->addLayout( l );
+ layOutButtonRow( l );
+ d->v->activate();
+}
+
+
+/*!
+ \reimp
+*/
+
+bool Q3Wizard::eventFilter( QObject * o, QEvent * e )
+{
+ return QDialog::eventFilter( o, e );
+}
+
+
+/*!
+ Removes \a page from the page sequence but does not delete the
+ page. If \a page is currently being displayed, Q3Wizard will
+ display the page that precedes it, or the first page if this was
+ the first page.
+*/
+
+void Q3Wizard::removePage( QWidget * page )
+{
+ if ( !page )
+ return;
+
+ int i = d->pages.count();
+ QWidget* cp = currentPage();
+ while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { }
+ if ( i < 0 )
+ return;
+ delete d->pages.takeAt(i);
+ page->hide();
+
+ if( cp == page ) {
+ i--;
+ if( i < 0 )
+ i = 0;
+ if ( pageCount() > 0 )
+ showPage( Q3Wizard::page( i ) );
+ } else if (pageCount() > 0) {
+ showPage(cp);
+ }
+}
+
+
+/*!
+ Returns a pointer to the page at position \a index in the
+ sequence, or 0 if \a index is out of range. The first page has
+ index 0.
+*/
+
+QWidget* Q3Wizard::page( int index ) const
+{
+ if ( index >= pageCount() || index < 0 )
+ return 0;
+ return d->pages.at( index )->w;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/dialogs/q3wizard.h b/src/qt3support/dialogs/q3wizard.h
new file mode 100644
index 0000000..9285eb3
--- /dev/null
+++ b/src/qt3support/dialogs/q3wizard.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3WIZARD_H
+#define Q3WIZARD_H
+
+#include <QtGui/qdialog.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QHBoxLayout;
+class Q3WizardPrivate;
+
+class Q_COMPAT_EXPORT Q3Wizard : public QDialog
+{
+ Q_OBJECT
+ Q_PROPERTY( QFont titleFont READ titleFont WRITE setTitleFont )
+
+public:
+ Q3Wizard( QWidget* parent=0, const char* name=0, bool modal=false, Qt::WindowFlags f=0 );
+ ~Q3Wizard();
+
+ void setVisible(bool);
+
+ void setFont( const QFont & font );
+
+ virtual void addPage( QWidget *, const QString & );
+ virtual void insertPage( QWidget*, const QString&, int );
+ virtual void removePage( QWidget * );
+
+ QString title( QWidget * ) const;
+ void setTitle( QWidget *, const QString & );
+ QFont titleFont() const;
+ void setTitleFont( const QFont & );
+
+ virtual void showPage( QWidget * );
+
+ QWidget * currentPage() const;
+
+ QWidget* page( int ) const;
+ int pageCount() const;
+ int indexOf( QWidget* ) const;
+
+ virtual bool appropriate( QWidget * ) const;
+ virtual void setAppropriate( QWidget *, bool );
+
+ QPushButton * backButton() const;
+ QPushButton * nextButton() const;
+ QPushButton * finishButton() const;
+ QPushButton * cancelButton() const;
+ QPushButton * helpButton() const;
+
+ bool eventFilter( QObject *, QEvent * );
+
+public Q_SLOTS:
+ virtual void setBackEnabled( QWidget *, bool );
+ virtual void setNextEnabled( QWidget *, bool );
+ virtual void setFinishEnabled( QWidget *, bool );
+
+ virtual void setHelpEnabled( QWidget *, bool );
+
+ // obsolete
+ virtual void setFinish( QWidget *, bool ) {}
+
+protected Q_SLOTS:
+ virtual void back();
+ virtual void next();
+ virtual void help();
+
+Q_SIGNALS:
+ void helpClicked();
+ void selected( const QString& );
+
+protected:
+ virtual void layOutButtonRow( QHBoxLayout * );
+ virtual void layOutTitleRow( QHBoxLayout *, const QString & );
+
+private:
+ void setBackEnabled( bool );
+ void setNextEnabled( bool );
+
+ void setHelpEnabled( bool );
+
+ void setNextPage( QWidget * );
+
+ void updateButtons();
+
+ void layOut();
+
+ Q3WizardPrivate *d;
+
+ Q_DISABLE_COPY(Q3Wizard)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3WIZARD_H
diff --git a/src/qt3support/itemviews/itemviews.pri b/src/qt3support/itemviews/itemviews.pri
new file mode 100644
index 0000000..cfc8c2d
--- /dev/null
+++ b/src/qt3support/itemviews/itemviews.pri
@@ -0,0 +1,11 @@
+# Qt compat module, old itemviews
+
+HEADERS += itemviews/q3iconview.h \
+ itemviews/q3listbox.h \
+ itemviews/q3listview.h \
+ itemviews/q3table.h
+
+SOURCES += itemviews/q3iconview.cpp \
+ itemviews/q3listbox.cpp \
+ itemviews/q3listview.cpp \
+ itemviews/q3table.cpp
diff --git a/src/qt3support/itemviews/q3iconview.cpp b/src/qt3support/itemviews/q3iconview.cpp
new file mode 100644
index 0000000..d7662be
--- /dev/null
+++ b/src/qt3support/itemviews/q3iconview.cpp
@@ -0,0 +1,6210 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#if defined(Q_CC_BOR)
+// needed for qsort() because of a std namespace problem on Borland
+#include "qplatformdefs.h"
+#endif
+
+#include "q3iconview.h"
+
+#ifndef QT_NO_ICONVIEW
+
+#include "private/q3richtext_p.h"
+#include "q3textedit.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qbrush.h"
+#include "q3cleanuphandler.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qfontmetrics.h"
+#include "qhash.h"
+#include "qimage.h"
+#include "qmime.h"
+#include "qpainter.h"
+#include "qpalette.h"
+#include "qpen.h"
+#include "qpixmapcache.h"
+#include "qstringlist.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "qtooltip.h"
+#include "q3strlist.h"
+
+#include <limits.h>
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+#define RECT_EXTENSION 300
+
+static const char * const unknown_xpm[] = {
+ "32 32 11 1",
+ "c c #ffffff",
+ "g c #c0c0c0",
+ "a c #c0ffc0",
+ "h c #a0a0a4",
+ "d c #585858",
+ "f c #303030",
+ "i c #400000",
+ "b c #00c000",
+ "e c #000000",
+ "# c #000000",
+ ". c None",
+ "...###..........................",
+ "...#aa##........................",
+ ".###baaa##......................",
+ ".#cde#baaa##....................",
+ ".#cccdeebaaa##..##f.............",
+ ".#cccccdeebaaa##aaa##...........",
+ ".#cccccccdeebaaaaaaaa##.........",
+ ".#cccccccccdeebaaaaaaa#.........",
+ ".#cccccgcgghhebbbbaaaaa#........",
+ ".#ccccccgcgggdebbbbbbaa#........",
+ ".#cccgcgcgcgghdeebiebbba#.......",
+ ".#ccccgcggggggghdeddeeba#.......",
+ ".#cgcgcgcggggggggghghdebb#......",
+ ".#ccgcggggggggghghghghd#b#......",
+ ".#cgcgcggggggggghghghhd#b#......",
+ ".#gcggggggggghghghhhhhd#b#......",
+ ".#cgcggggggggghghghhhhd#b#......",
+ ".#ggggggggghghghhhhhhhdib#......",
+ ".#gggggggggghghghhhhhhd#b#......",
+ ".#hhggggghghghhhhhhhhhd#b#......",
+ ".#ddhhgggghghghhhhhhhhd#b#......",
+ "..##ddhhghghhhhhhhhhhhdeb#......",
+ "....##ddhhhghhhhhhhhhhd#b#......",
+ "......##ddhhhhhhhhhhhhd#b#......",
+ "........##ddhhhhhhhhhhd#b#......",
+ "..........##ddhhhhhhhhd#b#......",
+ "............##ddhhhhhhd#b###....",
+ "..............##ddhhhhd#b#####..",
+ "................##ddhhd#b######.",
+ "..................##dddeb#####..",
+ "....................##d#b###....",
+ "......................####......"};
+
+static QPixmap *unknown_icon = 0;
+static QPixmap *qiv_buffer_pixmap = 0;
+#if !defined(Q_WS_X11)
+static QPixmap *qiv_selection = 0;
+#endif
+static bool optimize_layout = false;
+
+
+static void qt_cleanup_iv_pixmaps();
+typedef QList<QPixmap *> IVPixmaps;
+Q_GLOBAL_STATIC_WITH_INITIALIZER(IVPixmaps, qiv_pixmaps, qAddPostRoutine(qt_cleanup_iv_pixmaps))
+
+static void qt_cleanup_iv_pixmaps()
+{
+ qDeleteAll(*qiv_pixmaps());
+}
+
+static QPixmap *get_qiv_buffer_pixmap(const QSize &s)
+{
+ if (!qiv_buffer_pixmap) {
+ qiv_buffer_pixmap = new QPixmap(s);
+ qiv_pixmaps()->append(qiv_buffer_pixmap);
+ return qiv_buffer_pixmap;
+ }
+
+ qiv_buffer_pixmap->resize(s);
+ return qiv_buffer_pixmap;
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+class Q_COMPAT_EXPORT Q3IconDragData
+{
+public:
+ Q3IconDragData();
+ Q3IconDragData(const QRect &ir, const QRect &tr);
+
+ QRect pixmapRect() const;
+ QRect textRect() const;
+
+ void setPixmapRect(const QRect &r);
+ void setTextRect(const QRect &r);
+
+ QRect iconRect_, textRect_;
+ QString key_;
+
+ bool operator==(const Q3IconDragData &i) const;
+};
+
+class Q_COMPAT_EXPORT Q3IconDragDataItem
+{
+public:
+ Q3IconDragDataItem() {}
+ Q3IconDragDataItem(const Q3IconDragItem &i1, const Q3IconDragData &i2) : data(i1), item(i2) {}
+ Q3IconDragItem data;
+ Q3IconDragData item;
+ bool operator== (const Q3IconDragDataItem&) const;
+};
+
+class Q3IconDragPrivate
+{
+public:
+ QLinkedList<Q3IconDragDataItem> items;
+ static bool decode(QMimeSource* e, QLinkedList<Q3IconDragDataItem> &lst);
+};
+
+#endif
+
+class Q3IconViewPrivate
+{
+public:
+ Q3IconViewItem *firstItem, *lastItem;
+ uint count;
+ Q3IconView::SelectionMode selectionMode;
+ Q3IconViewItem *currentItem, *tmpCurrentItem, *highlightedItem,
+ *startDragItem, *pressedItem, *selectAnchor, *renamingItem;
+ QRect *rubber;
+ QTimer *scrollTimer, *adjustTimer, *updateTimer, *inputTimer,
+ *fullRedrawTimer;
+ int rastX, rastY, spacing;
+ int dragItems;
+ QPoint oldDragPos;
+ Q3IconView::Arrangement arrangement;
+ Q3IconView::ResizeMode resizeMode;
+ QSize oldSize;
+#ifndef QT_NO_DRAGANDDROP
+ QLinkedList<Q3IconDragDataItem> iconDragData;
+#endif
+ int numDragItems, cachedW, cachedH;
+ int maxItemWidth, maxItemTextLength;
+ QPoint dragStart;
+ QString currInputString;
+ Q3IconView::ItemTextPos itemTextPos;
+#ifndef QT_NO_CURSOR
+ QCursor oldCursor;
+#endif
+ int cachedContentsX, cachedContentsY;
+ QBrush itemTextBrush;
+ QRegion clipRegion;
+ QPoint dragStartPos;
+ QFontMetrics *fm;
+ int minLeftBearing, minRightBearing;
+
+ uint mousePressed : 1;
+ uint cleared : 1;
+ uint dropped : 1;
+ uint clearing : 1;
+ uint oldDragAcceptAction : 1;
+ uint isIconDrag : 1;
+ uint drawDragShapes : 1;
+ uint dirty : 1;
+ uint rearrangeEnabled : 1;
+ uint reorderItemsWhenInsert : 1;
+ uint drawAllBack : 1;
+ uint resortItemsWhenInsert : 1;
+ uint sortDirection : 1;
+ uint wordWrapIconText : 1;
+ uint containerUpdateLocked : 1;
+ uint firstSizeHint : 1;
+ uint showTips : 1;
+ uint pressedSelected : 1;
+ uint dragging : 1;
+ uint drawActiveSelection : 1;
+ uint inMenuMode : 1;
+
+ QPoint dragPos;
+ QPixmapCache maskCache;
+ QHash<Q3IconViewItem *, Q3IconViewItem *> selectedItems;
+
+ struct ItemContainer {
+ ItemContainer(ItemContainer *pr, ItemContainer *nx, const QRect &r)
+ : p(pr), n(nx), rect(r)
+ {
+ if (p)
+ p->n = this;
+ if (n)
+ n->p = this;
+ }
+ ItemContainer *p, *n;
+ QRect rect;
+ QList<Q3IconViewItem*> items;
+ } *firstContainer, *lastContainer;
+
+ struct SortableItem {
+ Q3IconViewItem *item;
+ };
+
+public:
+
+ /* finds the containers that intersect with \a searchRect in the direction \a dir relative to \a relativeTo */
+ QList<ItemContainer* >* findContainers(
+ Q3IconView:: Direction dir,
+ const QPoint &relativeTo,
+ const QRect &searchRect) const;
+ // friend int cmpIconViewItems(const void *n1, const void *n2);
+};
+
+
+QList<Q3IconViewPrivate::ItemContainer *>* Q3IconViewPrivate::findContainers(
+ Q3IconView:: Direction dir,
+ const QPoint &relativeTo,
+ const QRect &searchRect) const
+{
+
+ QList<Q3IconViewPrivate::ItemContainer *>* list =
+ new QList<Q3IconViewPrivate::ItemContainer*>();
+
+ if (arrangement == Q3IconView::LeftToRight) {
+ if (dir == Q3IconView::DirLeft || dir == Q3IconView::DirRight) {
+ ItemContainer *c = firstContainer;
+ for (; c; c = c->n)
+ if (c->rect.intersects(searchRect))
+ list->append(c);
+ } else {
+ if (dir == Q3IconView::DirDown) {
+ ItemContainer *c = firstContainer;
+ for (; c; c = c->n)
+ if (c->rect.intersects(searchRect) &&
+ c->rect.bottom() >= relativeTo.y())
+ list->append(c);
+ } else {
+ ItemContainer *c = lastContainer;
+ for (; c; c = c->p)
+ if (c->rect.intersects(searchRect) &&
+ c->rect.top() <= relativeTo.y())
+ list->append(c);
+ }
+ }
+ } else {
+ if (dir == Q3IconView::DirUp || dir == Q3IconView::DirDown) {
+ ItemContainer *c = firstContainer;
+ for (; c; c = c->n)
+ if (c->rect.intersects(searchRect))
+ list->append(c);
+ } else {
+ if (dir == Q3IconView::DirRight) {
+ ItemContainer *c = firstContainer;
+ for (; c; c = c->n)
+ if (c->rect.intersects(searchRect) &&
+ c->rect.right() >= relativeTo.x())
+ list->append(c);
+ } else {
+ ItemContainer *c = lastContainer;
+ for (; c; c = c->p)
+ if (c->rect.intersects(searchRect) &&
+ c->rect.left() <= relativeTo.x())
+ list->append(c);
+ }
+ }
+ }
+ return list;
+}
+
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int _cdecl cmpIconViewItems(const void *n1, const void *n2)
+#else
+static int cmpIconViewItems(const void *n1, const void *n2)
+#endif
+{
+ if (!n1 || !n2)
+ return 0;
+
+ Q3IconViewPrivate::SortableItem *i1 = (Q3IconViewPrivate::SortableItem *)n1;
+ Q3IconViewPrivate::SortableItem *i2 = (Q3IconViewPrivate::SortableItem *)n2;
+
+ return i1->item->compare(i2->item);
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+class Q3IconViewItemPrivate
+{
+public:
+ Q3IconViewPrivate::ItemContainer *container1, *container2;
+};
+
+#ifndef QT_NO_TEXTEDIT
+
+class Q3IconViewItemLineEdit : public Q3TextEdit
+{
+ friend class Q3IconViewItem;
+
+public:
+ Q3IconViewItemLineEdit(const QString &text, QWidget *parent, Q3IconViewItem *theItem, const char* name=0);
+
+protected:
+ void keyPressEvent(QKeyEvent *e);
+ void focusOutEvent(QFocusEvent *e);
+
+protected:
+ Q3IconViewItem *item;
+ QString startText;
+
+private:
+ Q_DISABLE_COPY(Q3IconViewItemLineEdit)
+};
+
+Q3IconViewItemLineEdit::Q3IconViewItemLineEdit(const QString &text, QWidget *parent,
+ Q3IconViewItem *theItem, const char *name)
+ : Q3TextEdit(parent, name), item(theItem), startText(text)
+{
+ setFrameStyle(QFrame::Plain | QFrame::Box);
+ setLineWidth(1);
+
+ setHScrollBarMode(AlwaysOff);
+ setVScrollBarMode(AlwaysOff);
+
+ setWordWrap(WidgetWidth);
+ setWrapColumnOrWidth(item->iconView()->maxItemWidth() -
+ (item->iconView()->itemTextPos() == Q3IconView::Bottom ?
+ 0 : item->pixmapRect().width()));
+ document()->formatter()->setAllowBreakInWords(true);
+ resize(200, 200); // ### some size, there should be a forceReformat()
+ setTextFormat(Qt::PlainText);
+ setText(text);
+ setAlignment(Qt::AlignCenter);
+
+ resize(wrapColumnOrWidth() + 2, heightForWidth(wrapColumnOrWidth()) + 2);
+}
+
+void Q3IconViewItemLineEdit::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_Escape) {
+ item->Q3IconViewItem::setText(startText);
+ item->cancelRenameItem();
+ } else if (e->key() == Qt::Key_Enter ||
+ e->key() == Qt::Key_Return) {
+ item->renameItem();
+ } else {
+ Q3TextEdit::keyPressEvent(e);
+ sync();
+ resize(width(), document()->height() + 2);
+
+ }
+}
+
+void Q3IconViewItemLineEdit::focusOutEvent(QFocusEvent *e)
+{
+ Q_UNUSED(e) // I need this to get rid of a Borland warning
+ if (e->reason() != Qt::PopupFocusReason)
+ item->cancelRenameItem();
+}
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+
+
+/*!
+ \class Q3IconDragItem
+ \brief The Q3IconDragItem class encapsulates a drag item.
+ \compat
+
+ The Q3IconDrag class uses a list of Q3IconDragItems to support drag
+ and drop operations.
+
+ In practice a Q3IconDragItem object (or an object of a class derived
+ from Q3IconDragItem) is created for each icon view item which is
+ dragged. Each of these Q3IconDragItems is stored in a Q3IconDrag
+ object.
+
+ See Q3IconView::dragObject() for more information.
+*/
+
+/*!
+ Constructs a Q3IconDragItem with no data.
+*/
+
+Q3IconDragItem::Q3IconDragItem()
+{
+ ba = "no data";
+}
+
+/*!
+ Destructor.
+*/
+
+Q3IconDragItem::~Q3IconDragItem()
+{
+}
+
+/*!
+ Returns the data contained in the Q3IconDragItem.
+*/
+
+QByteArray Q3IconDragItem::data() const
+{
+ return ba;
+}
+
+/*!
+ Sets the data for the Q3IconDragItem to the data stored in the
+ QByteArray \a d.
+*/
+
+void Q3IconDragItem::setData(const QByteArray &d)
+{
+ ba = d;
+}
+
+/*!
+ \internal
+*/
+
+bool Q3IconDragItem::operator==(const Q3IconDragItem &i) const
+{
+ return ba == i.ba;
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3IconDragDataItem::operator==(const Q3IconDragDataItem &i) const
+{
+ return (i.item == item &&
+ i.data == data);
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3IconDragData::operator==(const Q3IconDragData &i) const
+{
+ return key_ == i.key_;
+}
+
+
+/*!
+ \class Q3IconDrag
+ \brief The Q3IconDrag class supports drag and drop operations
+ within a Q3IconView.
+
+ \compat
+
+ A Q3IconDrag object is used to maintain information about the
+ positions of dragged items and the data associated with them.
+ Q3IconViews are able to use this information to paint the dragged
+ items in the correct positions. Internally, Q3IconDrag stores the
+ data associated with drag items in Q3IconDragItem objects.
+
+ If you want to use the extended drag and drop functionality of
+ Q3IconView, create a Q3IconDrag object in a reimplementation of
+ Q3IconView::dragObject(). Then create a Q3IconDragItem for each item
+ which should be dragged, set the data it represents with
+ Q3IconDragItem::setData(), and add each Q3IconDragItem to the drag
+ object using append().
+
+ The data in Q3IconDragItems is stored in a QByteArray and is
+ mime-typed (see QMimeSource and the
+ \link http://qt.nokia.com/doc/dnd.html Drag and Drop\endlink
+ overview). If you want to use your own mime-types derive a class
+ from Q3IconDrag and reimplement format(), encodedData() and
+ canDecode().
+
+ The fileiconview example program demonstrates the use of the
+ Q3IconDrag class including subclassing and reimplementing
+ dragObject(), format(), encodedData() and canDecode().
+
+ \sa QMimeSource::format()
+*/
+
+/*!
+ Constructs a drag object called \a name, which is a child of \a
+ dragSource.
+
+ Note that the drag object will be deleted when \a dragSource is deleted.
+*/
+
+Q3IconDrag::Q3IconDrag(QWidget * dragSource, const char* name)
+ : Q3DragObject(dragSource, name)
+{
+ d = new Q3IconDragPrivate;
+}
+
+/*!
+ Destructor.
+*/
+
+Q3IconDrag::~Q3IconDrag()
+{
+ delete d;
+}
+
+/*!
+ Append the Q3IconDragItem, \a i, to the Q3IconDrag object's list of
+ items. You must also supply the geometry of the pixmap, \a pr, and
+ the textual caption, \a tr.
+
+ \sa Q3IconDragItem
+*/
+
+void Q3IconDrag::append(const Q3IconDragItem &i, const QRect &pr, const QRect &tr)
+{
+ d->items.append(Q3IconDragDataItem(i, Q3IconDragData(pr, tr)));
+}
+
+/*!
+ \reimp
+*/
+
+const char* Q3IconDrag::format(int i) const
+{
+ if (i == 0)
+ return "application/x-qiconlist";
+ return 0;
+}
+
+/*!
+ Returns the encoded data of the drag object if \a mime is
+ application/x-qiconlist.
+*/
+
+QByteArray Q3IconDrag::encodedData(const char* mime) const
+{
+ if (d->items.size() <= 0 || QString::fromLatin1(mime) !=
+ QString::fromLatin1("application/x-qiconlist"))
+ return QByteArray();
+
+ QLinkedList<Q3IconDragDataItem>::ConstIterator it = d->items.begin();
+ QString s;
+ for (; it != d->items.end(); ++it) {
+ QString k(QLatin1String("%1$@@$%2$@@$%3$@@$%4$@@$%5$@@$%6$@@$%7$@@$%8$@@$"));
+ k = k.arg((*it).item.pixmapRect().x()).arg(
+ (*it).item.pixmapRect().y()).arg((*it).item.pixmapRect().width()).
+ arg((*it).item.pixmapRect().height()).arg(
+ (*it).item.textRect().x()).arg((*it).item.textRect().y()).
+ arg((*it).item.textRect().width()).arg(
+ (*it).item.textRect().height());
+ k += QString::fromLatin1((*it).data.data()) + QLatin1String("$@@$");
+ s += k;
+ }
+
+ QByteArray a;
+ a.resize(s.length() + 1);
+ memcpy(a.data(), s.latin1(), a.size());
+ return a;
+}
+
+/*!
+ Returns true if \a e can be decoded by the Q3IconDrag, otherwise
+ return false.
+*/
+
+bool Q3IconDrag::canDecode(QMimeSource* e)
+{
+ if (e->provides("application/x-qiconlist"))
+ return true;
+ return false;
+}
+
+/*!
+ Decodes the data which is stored (encoded) in \a e and, if
+ successful, fills the \a list of icon drag items with the decoded
+ data. Returns true if there was some data, false otherwise.
+*/
+
+bool Q3IconDragPrivate::decode(QMimeSource* e, QLinkedList<Q3IconDragDataItem> &lst)
+{
+ QByteArray ba = e->encodedData("application/x-qiconlist");
+ if (ba.size()) {
+ lst.clear();
+ // #### unicode clean????
+ QString s = QString::fromLatin1(ba);
+ Q3IconDragDataItem item;
+ QRect ir, tr;
+ QStringList l = QStringList::split(QLatin1String("$@@$"), s);
+
+ int i = 0;
+ QStringList::Iterator it = l.begin();
+ for (; it != l.end(); ++it) {
+ if (i == 0) {
+ ir.setX((*it).toInt());
+ } else if (i == 1) {
+ ir.setY((*it).toInt());
+ } else if (i == 2) {
+ ir.setWidth((*it).toInt());
+ } else if (i == 3) {
+ ir.setHeight((*it).toInt());
+ } else if (i == 4) {
+ tr.setX((*it).toInt());
+ } else if (i == 5) {
+ tr.setY((*it).toInt());
+ } else if (i == 6) {
+ tr.setWidth((*it).toInt());
+ } else if (i == 7) {
+ tr.setHeight((*it).toInt());
+ } else if (i == 8) {
+ QByteArray d;
+ d.resize((*it).length());
+ memcpy(d.data(), (*it).latin1(), (*it).length());
+ item.item.setPixmapRect(ir);
+ item.item.setTextRect(tr);
+ item.data.setData(d);
+ lst.append(item);
+ }
+ ++i;
+ if (i > 8)
+ i = 0;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+Q3IconDragData::Q3IconDragData()
+ : iconRect_(), textRect_()
+{
+}
+
+Q3IconDragData::Q3IconDragData(const QRect &ir, const QRect &tr)
+ : iconRect_(ir), textRect_(tr)
+{
+}
+
+QRect Q3IconDragData::textRect() const
+{
+ return textRect_;
+}
+
+QRect Q3IconDragData::pixmapRect() const
+{
+ return iconRect_;
+}
+
+void Q3IconDragData::setPixmapRect(const QRect &r)
+{
+ iconRect_ = r;
+}
+
+void Q3IconDragData::setTextRect(const QRect &r)
+{
+ textRect_ = r;
+}
+
+#endif
+
+
+/*!
+ \class Q3IconViewItem
+ \brief The Q3IconViewItem class provides a single item in a Q3IconView.
+
+ \compat
+
+ A Q3IconViewItem contains an icon, a string and optionally a sort
+ key, and can display itself in a Q3IconView.
+
+ The simplest way to create a Q3IconViewItem and insert it into a
+ Q3IconView is to construct the item passing the constructor a
+ pointer to the icon view, a string and an icon:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 0
+
+ By default the text of an icon view item may not be edited by the
+ user but calling setRenameEnabled(true) will allow the user to
+ perform in-place editing of the item's text.
+
+ When the icon view is deleted all items in it are deleted
+ automatically.
+
+ The Q3IconView::firstItem() and Q3IconViewItem::nextItem() functions
+ provide a means of iterating over all the items in a Q3IconView:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 1
+
+ The item's icon view is available from iconView(), and its
+ position in the icon view from index().
+
+ The item's selection status is available from isSelected() and is
+ set and controlled by setSelected() and isSelectable().
+
+ The text and icon can be set with setText() and setPixmap() and
+ retrieved with text() and pixmap(). The item's sort key defaults
+ to text() but may be set with setKey() and retrieved with key().
+ The comparison function, compare() uses key().
+
+ Items may be repositioned with move() and moveBy(). An item's
+ geometry is available from rect(), x(), y(), width(), height(),
+ size(), pos(), textRect() and pixmapRect(). You can also test
+ against the position of a point with contains() and intersects().
+
+ To remove an item from an icon view, just delete the item. The
+ Q3IconViewItem destructor removes it cleanly from its icon view.
+
+ Because the icon view is designed to use drag-and-drop, the icon
+ view item also has functions for drag-and-drop which may be
+ reimplemented.
+
+ The class is designed to be very similar to Q3ListView and Q3ListBox
+ in use, both via instantiation and subclassing.
+*/
+
+/*!
+ Constructs a Q3IconViewItem and inserts it into icon view \a parent
+ with no text and a default icon.
+*/
+
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent)
+ : view(parent), itemText(), itemIcon(unknown_icon)
+{
+ init();
+}
+
+/*!
+ Constructs a Q3IconViewItem and inserts it into the icon view \a
+ parent with no text and a default icon, after the icon view item
+ \a after.
+*/
+
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after)
+ : view(parent), itemText(), itemIcon(unknown_icon),
+ prev(0), next(0)
+{
+ init(after);
+}
+
+/*!
+ Constructs an icon view item and inserts it into the icon view \a
+ parent using \a text as the text and a default icon.
+*/
+
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text)
+ : view(parent), itemText(text), itemIcon(unknown_icon)
+{
+ init(0);
+}
+
+/*!
+ Constructs an icon view item and inserts it into the icon view \a
+ parent using \a text as the text and a default icon, after the
+ icon view item \a after.
+*/
+
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after,
+ const QString &text)
+ : view(parent), itemText(text), itemIcon(unknown_icon)
+{
+ init(after);
+}
+
+/*!
+ Constructs an icon view item and inserts it into the icon view \a
+ parent using \a text as the text and \a icon as the icon.
+*/
+
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text,
+ const QPixmap &icon)
+ : view(parent),
+ itemText(text), itemIcon(new QPixmap(icon))
+{
+ init(0);
+}
+
+
+/*!
+ Constructs an icon view item and inserts it into the icon view \a
+ parent using \a text as the text and \a icon as the icon, after
+ the icon view item \a after.
+*/
+
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after,
+ const QString &text, const QPixmap &icon)
+ : view(parent), itemText(text), itemIcon(new QPixmap(icon))
+{
+ init(after);
+}
+
+/*!
+ Constructs an icon view item and inserts it into the icon view \a
+ parent using \a text as the text and \a picture as the icon.
+*/
+
+#ifndef QT_NO_PICTURE
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text,
+ const QPicture &picture)
+ : view(parent), itemText(text), itemIcon(0)
+{
+ init(0, new QPicture(picture));
+}
+
+/*!
+ Constructs an icon view item and inserts it into the icon view \a
+ parent using \a text as the text and \a picture as the icon, after
+ the icon view item \a after.
+*/
+
+Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after,
+ const QString &text, const QPicture &picture)
+ : view(parent), itemText(text), itemIcon(0)
+{
+ init(after, new QPicture(picture));
+}
+#endif
+
+/*!
+ This private function initializes the icon view item and inserts it
+ into the icon view.
+*/
+
+void Q3IconViewItem::init(Q3IconViewItem *after
+#ifndef QT_NO_PICTURE
+ , QPicture *pic
+#endif
+ )
+{
+ d = new Q3IconViewItemPrivate;
+ d->container1 = 0;
+ d->container2 = 0;
+ prev = next = 0;
+ allow_rename = false;
+ allow_drag = true;
+ allow_drop = true;
+ selected = false;
+ selectable = true;
+#ifndef QT_NO_TEXTEDIT
+ renameBox = 0;
+#endif
+#ifndef QT_NO_PICTURE
+ itemPic = pic;
+#endif
+ if (view) {
+ itemKey = itemText;
+ dirty = true;
+ wordWrapDirty = true;
+ itemRect = QRect(-1, -1, 0, 0);
+ calcRect();
+ view->insertItem(this, after);
+ }
+}
+
+/*!
+ Destroys the icon view item and tells the parent icon view that
+ the item has been destroyed.
+*/
+
+Q3IconViewItem::~Q3IconViewItem()
+{
+#ifndef QT_NO_TEXTEDIT
+ removeRenameBox();
+#endif
+ if (view && !view->d->clearing)
+ view->takeItem(this);
+ view = 0;
+ if (itemIcon && itemIcon->serialNumber() != unknown_icon->serialNumber())
+ delete itemIcon;
+#ifndef QT_NO_PICTURE
+ delete itemPic;
+#endif
+ delete d;
+}
+
+int Q3IconViewItem::RTTI = 0;
+
+/*!
+ Returns 0.
+
+ Make your derived classes return their own values for rtti(), so
+ that you can distinguish between icon view item types. You should
+ use values greater than 1000, preferably a large random number, to
+ allow for extensions to this class.
+*/
+
+int Q3IconViewItem::rtti() const
+{
+ return RTTI;
+}
+
+
+/*!
+ Sets \a text as the text of the icon view item. This function
+ might be a no-op if you reimplement text().
+
+ \sa text()
+*/
+
+void Q3IconViewItem::setText(const QString &text)
+{
+ if (text == itemText)
+ return;
+
+ wordWrapDirty = true;
+ itemText = text;
+ if (itemKey.isEmpty())
+ itemKey = itemText;
+
+ QRect oR = rect();
+ calcRect();
+ oR = oR.united(rect());
+
+ if (view) {
+ if (QRect(view->contentsX(), view->contentsY(),
+ view->visibleWidth(), view->visibleHeight()).
+ intersects(oR))
+ view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2);
+ }
+}
+
+/*!
+ Sets \a k as the sort key of the icon view item. By default
+ text() is used for sorting.
+
+ \sa compare()
+*/
+
+void Q3IconViewItem::setKey(const QString &k)
+{
+ if (k == itemKey)
+ return;
+
+ itemKey = k;
+}
+
+/*!
+ Sets \a icon as the item's icon in the icon view. This function
+ might be a no-op if you reimplement pixmap().
+
+ \sa pixmap()
+*/
+
+void Q3IconViewItem::setPixmap(const QPixmap &icon)
+{
+ if (itemIcon && itemIcon == unknown_icon)
+ itemIcon = 0;
+
+ if (itemIcon)
+ *itemIcon = icon;
+ else
+ itemIcon = new QPixmap(icon);
+ QRect oR = rect();
+ calcRect();
+ oR = oR.united(rect());
+
+ if (view) {
+ if (QRect(view->contentsX(), view->contentsY(),
+ view->visibleWidth(), view->visibleHeight()).
+ intersects(oR))
+ view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2);
+ }
+}
+
+/*!
+ Sets \a icon as the item's icon in the icon view. This function
+ might be a no-op if you reimplement picture().
+
+ \sa picture()
+*/
+
+#ifndef QT_NO_PICTURE
+void Q3IconViewItem::setPicture(const QPicture &icon)
+{
+ // clear assigned pixmap if any
+ if (itemIcon) {
+ if (itemIcon == unknown_icon) {
+ itemIcon = 0;
+ } else {
+ delete itemIcon;
+ itemIcon = 0;
+ }
+ }
+ if (itemPic)
+ delete itemPic;
+ itemPic = new QPicture(icon);
+
+ QRect oR = rect();
+ calcRect();
+ oR = oR.united(rect());
+
+ if (view) {
+ if (QRect(view->contentsX(), view->contentsY(),
+ view->visibleWidth(), view->visibleHeight()).
+ intersects(oR))
+ view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2);
+ }
+}
+#endif
+
+/*!
+ \overload
+
+ Sets \a text as the text of the icon view item. If \a recalc is
+ true, the icon view's layout is recalculated. If \a redraw is true
+ (the default), the icon view is repainted.
+
+ \sa text()
+*/
+
+void Q3IconViewItem::setText(const QString &text, bool recalc, bool redraw)
+{
+ if (text == itemText)
+ return;
+
+ wordWrapDirty = true;
+ itemText = text;
+
+ if (recalc)
+ calcRect();
+ if (redraw)
+ repaint();
+}
+
+/*!
+ \overload
+
+ Sets \a icon as the item's icon in the icon view. If \a recalc is
+ true, the icon view's layout is recalculated. If \a redraw is true
+ (the default), the icon view is repainted.
+
+ \sa pixmap()
+*/
+
+void Q3IconViewItem::setPixmap(const QPixmap &icon, bool recalc, bool redraw)
+{
+ if (itemIcon && itemIcon == unknown_icon)
+ itemIcon = 0;
+
+ if (itemIcon)
+ *itemIcon = icon;
+ else
+ itemIcon = new QPixmap(icon);
+
+ if (redraw) {
+ if (recalc) {
+ QRect oR = rect();
+ calcRect();
+ oR = oR.united(rect());
+
+ if (view) {
+ if (QRect(view->contentsX(), view->contentsY(),
+ view->visibleWidth(), view->visibleHeight()).
+ intersects(oR))
+ view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2);
+ }
+ } else {
+ repaint();
+ }
+ } else if (recalc) {
+ calcRect();
+ }
+}
+
+/*!
+ If \a allow is true, the user can rename the icon view item by
+ clicking on the text (or pressing F2) while the item is selected
+ (in-place renaming). If \a allow is false, in-place renaming is
+ not possible.
+*/
+
+void Q3IconViewItem::setRenameEnabled(bool allow)
+{
+ allow_rename = (uint)allow;
+}
+
+/*!
+ If \a allow is true, the icon view permits the user to drag the
+ icon view item either to another position within the icon view or
+ to somewhere outside of it. If \a allow is false, the item cannot
+ be dragged.
+*/
+
+void Q3IconViewItem::setDragEnabled(bool allow)
+{
+ allow_drag = (uint)allow;
+}
+
+/*!
+ If \a allow is true, the icon view lets the user drop something on
+ this icon view item.
+*/
+
+void Q3IconViewItem::setDropEnabled(bool allow)
+{
+ allow_drop = (uint)allow;
+}
+
+/*!
+ Returns the text of the icon view item. Normally you set the text
+ of the item with setText(), but sometimes it's inconvenient to
+ call setText() for every item; so you can subclass Q3IconViewItem,
+ reimplement this function, and return the text of the item. If you
+ do this, you must call calcRect() manually each time the text
+ (and therefore its size) changes.
+
+ \sa setText()
+*/
+
+QString Q3IconViewItem::text() const
+{
+ return itemText;
+}
+
+/*!
+ Returns the key of the icon view item or text() if no key has been
+ explicitly set.
+
+ \sa setKey(), compare()
+*/
+
+QString Q3IconViewItem::key() const
+{
+ return itemKey;
+}
+
+/*!
+ Returns the icon of the icon view item if it is a pixmap, or 0 if
+ it is a picture. In the latter case use picture() instead.
+ Normally you set the pixmap of the item with setPixmap(), but
+ sometimes it's inconvenient to call setPixmap() for every item. So
+ you can subclass Q3IconViewItem, reimplement this function and
+ return a pointer to the item's pixmap. If you do this, you \e must
+ call calcRect() manually each time the size of this pixmap
+ changes.
+
+ \sa setPixmap()
+*/
+
+QPixmap *Q3IconViewItem::pixmap() const
+{
+ return itemIcon;
+}
+
+/*!
+ Returns the icon of the icon view item if it is a picture, or 0 if
+ it is a pixmap. In the latter case use pixmap() instead. Normally
+ you set the picture of the item with setPicture(), but sometimes
+ it's inconvenient to call setPicture() for every item. So you can
+ subclass Q3IconViewItem, reimplement this function and return a
+ pointer to the item's picture. If you do this, you \e must call
+ calcRect() manually each time the size of this picture changes.
+
+ \sa setPicture()
+*/
+
+#ifndef QT_NO_PICTURE
+QPicture *Q3IconViewItem::picture() const
+{
+ return itemPic;
+}
+#endif
+
+/*!
+ Returns true if the item can be renamed by the user with in-place
+ renaming; otherwise returns false.
+
+ \sa setRenameEnabled()
+*/
+
+bool Q3IconViewItem::renameEnabled() const
+{
+ return (bool)allow_rename;
+}
+
+/*!
+ Returns true if the user is allowed to drag the icon view item;
+ otherwise returns false.
+
+ \sa setDragEnabled()
+*/
+
+bool Q3IconViewItem::dragEnabled() const
+{
+ return (bool)allow_drag;
+}
+
+/*!
+ Returns true if the user is allowed to drop something onto the
+ item; otherwise returns false.
+
+ \sa setDropEnabled()
+*/
+
+bool Q3IconViewItem::dropEnabled() const
+{
+ return (bool)allow_drop;
+}
+
+/*!
+ Returns a pointer to this item's icon view parent.
+*/
+
+Q3IconView *Q3IconViewItem::iconView() const
+{
+ return view;
+}
+
+/*!
+ Returns a pointer to the previous item, or 0 if this is the first
+ item in the icon view.
+
+ \sa nextItem() Q3IconView::firstItem()
+*/
+
+Q3IconViewItem *Q3IconViewItem::prevItem() const
+{
+ return prev;
+}
+
+/*!
+ Returns a pointer to the next item, or 0 if this is the last item
+ in the icon view.
+
+ To find the first item use Q3IconView::firstItem().
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 2
+
+ \sa prevItem()
+*/
+
+Q3IconViewItem *Q3IconViewItem::nextItem() const
+{
+ return next;
+}
+
+/*!
+ Returns the index of this item in the icon view, or -1 if an error
+ occurred.
+*/
+
+int Q3IconViewItem::index() const
+{
+ if (view)
+ return view->index(this);
+
+ return -1;
+}
+
+
+
+/*!
+ \overload
+
+ This variant is equivalent to calling the other variant with \e cb
+ set to false.
+*/
+
+void Q3IconViewItem::setSelected(bool s)
+{
+ setSelected(s, false);
+}
+
+/*!
+ Selects or unselects the item, depending on \a s; it may also
+ unselect other items, depending on Q3IconView::selectionMode() and
+ \a cb.
+
+ If \a s is false, the item is unselected.
+
+ If \a s is true and Q3IconView::selectionMode() is
+ Q3IconView::Single, the item is selected and the item previously
+ selected is unselected.
+
+ If \a s is true and Q3IconView::selectionMode() is
+ Q3IconView::Extended, the item is selected. If \a cb is true, the
+ selection state of the other items is left unchanged. If \a cb is
+ false (the default) all other items are unselected.
+
+ If \a s is true and Q3IconView::selectionMode() is
+ Q3IconView::Multi, the item is selected.
+
+ Note that \a cb is used only if Q3IconView::selectionMode() is
+ Q3IconView::Extended; cb defaults to false.
+
+ All items whose selection status changes repaint themselves.
+*/
+
+void Q3IconViewItem::setSelected(bool s, bool cb)
+{
+ if (!view)
+ return;
+ if (view->selectionMode() != Q3IconView::NoSelection &&
+ selectable && s != (bool)selected) {
+
+ if (view->d->selectionMode == Q3IconView::Single && this != view->d->currentItem) {
+ Q3IconViewItem *o = view->d->currentItem;
+ if (o && o->selected)
+ o->selected = false;
+ view->d->currentItem = this;
+ if (o)
+ o->repaint();
+ emit view->currentChanged(this);
+ }
+
+ if (!s) {
+ selected = false;
+ } else {
+ if (view->d->selectionMode == Q3IconView::Single && view->d->currentItem) {
+ view->d->currentItem->selected = false;
+ }
+ if ((view->d->selectionMode == Q3IconView::Extended && !cb) ||
+ view->d->selectionMode == Q3IconView::Single) {
+ bool b = view->signalsBlocked();
+ view->blockSignals(true);
+ view->selectAll(false);
+ view->blockSignals(b);
+ }
+ selected = s;
+ }
+
+ repaint();
+ if (!view->signalsBlocked()) {
+ bool emitIt = view->d->selectionMode == Q3IconView::Single && s;
+ Q3IconView *v = view;
+ emit v->selectionChanged();
+ if (emitIt)
+ emit v->selectionChanged(this);
+ }
+ }
+}
+
+/*!
+ Sets this item to be selectable if \a enable is true (the default)
+ or unselectable if \a enable is false.
+
+ The user is unable to select a non-selectable item using either
+ the keyboard or the mouse. (The application programmer can select
+ an item in code regardless of this setting.)
+
+ \sa isSelectable()
+*/
+
+void Q3IconViewItem::setSelectable(bool enable)
+{
+ selectable = (uint)enable;
+}
+
+/*!
+ Returns true if the item is selected; otherwise returns false.
+
+ \sa setSelected()
+*/
+
+bool Q3IconViewItem::isSelected() const
+{
+ return (bool)selected;
+}
+
+/*!
+ Returns true if the item is selectable; otherwise returns false.
+
+ \sa setSelectable()
+*/
+
+bool Q3IconViewItem::isSelectable() const
+{
+ return (bool)selectable;
+}
+
+/*!
+ Repaints the item.
+*/
+
+void Q3IconViewItem::repaint()
+{
+ if (view)
+ view->repaintItem(this);
+}
+
+/*!
+ Moves the item to position (\a x, \a y) in the icon view (these
+ are contents coordinates). Returns true if the item is moved.
+ Returns false if the item is already at the specified position.
+*/
+
+bool Q3IconViewItem::move(int x, int y)
+{
+ if (x == this->x() && y == this->y())
+ return false;
+ itemRect.setRect(x, y, itemRect.width(), itemRect.height());
+ checkRect();
+ if (view)
+ view->updateItemContainer(this);
+ return true;
+}
+
+/*!
+ Moves the item \a dx pixels in the x-direction and \a dy pixels in
+ the y-direction.
+*/
+
+void Q3IconViewItem::moveBy(int dx, int dy)
+{
+ itemRect.moveBy(dx, dy);
+ checkRect();
+ if (view)
+ view->updateItemContainer(this);
+}
+
+/*!
+ \overload
+
+ Moves the item to the point \a pnt.
+*/
+
+bool Q3IconViewItem::move(const QPoint &pnt)
+{
+ return move(pnt.x(), pnt.y());
+}
+
+/*!
+ \overload
+
+ Moves the item by the x, y values in point \a pnt.
+*/
+
+void Q3IconViewItem::moveBy(const QPoint &pnt)
+{
+ moveBy(pnt.x(), pnt.y());
+}
+
+/*!
+ Returns the bounding rectangle of the item (in contents
+ coordinates).
+*/
+
+QRect Q3IconViewItem::rect() const
+{
+ return itemRect;
+}
+
+/*!
+ Returns the x-coordinate of the item (in contents coordinates).
+*/
+
+int Q3IconViewItem::x() const
+{
+ return itemRect.x();
+}
+
+/*!
+ Returns the y-coordinate of the item (in contents coordinates).
+*/
+
+int Q3IconViewItem::y() const
+{
+ return itemRect.y();
+}
+
+/*!
+ Returns the width of the item.
+*/
+
+int Q3IconViewItem::width() const
+{
+ return qMax(itemRect.width(), QApplication::globalStrut().width());
+}
+
+/*!
+ Returns the height of the item.
+*/
+
+int Q3IconViewItem::height() const
+{
+ return qMax(itemRect.height(), QApplication::globalStrut().height());
+}
+
+/*!
+ Returns the size of the item.
+*/
+
+QSize Q3IconViewItem::size() const
+{
+ return QSize(itemRect.width(), itemRect.height());
+}
+
+/*!
+ Returns the position of the item (in contents coordinates).
+*/
+
+QPoint Q3IconViewItem::pos() const
+{
+ return QPoint(itemRect.x(), itemRect.y());
+}
+
+/*!
+ Returns the bounding rectangle of the item's text.
+
+ If \a relative is true, (the default), the returned rectangle is
+ relative to the origin of the item's rectangle. If \a relative is
+ false, the returned rectangle is relative to the origin of the
+ icon view's contents coordinate system.
+*/
+
+QRect Q3IconViewItem::textRect(bool relative) const
+{
+ if (relative)
+ return itemTextRect;
+ else
+ return QRect(x() + itemTextRect.x(), y() + itemTextRect.y(), itemTextRect.width(), itemTextRect.height());
+}
+
+/*!
+ Returns the bounding rectangle of the item's icon.
+
+ If \a relative is true, (the default), the rectangle is relative to
+ the origin of the item's rectangle. If \a relative is false, the
+ returned rectangle is relative to the origin of the icon view's
+ contents coordinate system.
+*/
+
+QRect Q3IconViewItem::pixmapRect(bool relative) const
+{
+ if (relative)
+ return itemIconRect;
+ else
+ return QRect(x() + itemIconRect.x(), y() + itemIconRect.y(), itemIconRect.width(), itemIconRect.height());
+}
+
+/*!
+ Returns true if the item contains the point \a pnt (in contents
+ coordinates); otherwise returns false.
+*/
+
+bool Q3IconViewItem::contains(const QPoint& pnt) const
+{
+ QRect textArea = textRect(false);
+ QRect pixmapArea = pixmapRect(false);
+ if (iconView()->itemTextPos() == Q3IconView::Bottom)
+ textArea.setTop(pixmapArea.bottom());
+ else
+ textArea.setLeft(pixmapArea.right());
+ return textArea.contains(pnt) || pixmapArea.contains(pnt);
+}
+
+/*!
+ Returns true if the item intersects the rectangle \a r (in
+ contents coordinates); otherwise returns false.
+*/
+
+bool Q3IconViewItem::intersects(const QRect& r) const
+{
+ return (textRect(false).intersects(r) ||
+ pixmapRect(false).intersects(r));
+}
+
+/*!
+ \fn bool Q3IconViewItem::acceptDrop(const QMimeSource *mime) const
+
+ Returns true if you can drop things with a QMimeSource of \a mime
+ onto this item; otherwise returns false.
+
+ The default implementation always returns false. You must subclass
+ Q3IconViewItem and reimplement acceptDrop() to accept drops.
+*/
+
+bool Q3IconViewItem::acceptDrop(const QMimeSource *) const
+{
+ return false;
+}
+
+#ifndef QT_NO_TEXTEDIT
+/*!
+ Starts in-place renaming of an icon, if allowed.
+
+ This function sets up the icon view so that the user can edit the
+ item text, and then returns. When the user is done, setText() will
+ be called and Q3IconView::itemRenamed() will be emitted (unless the
+ user canceled, e.g. by pressing the Escape key).
+
+ \sa setRenameEnabled()
+*/
+
+void Q3IconViewItem::rename()
+{
+ if (!view)
+ return;
+ if (renameBox)
+ removeRenameBox();
+ oldRect = rect();
+ renameBox = new Q3IconViewItemLineEdit(itemText, view->viewport(), this, "qt_renamebox");
+ iconView()->ensureItemVisible(this);
+ QRect tr(textRect(false));
+ view->addChild(renameBox, tr.x() + (tr.width() / 2 - renameBox->width() / 2), tr.y() - 3);
+ renameBox->selectAll();
+ view->viewport()->setFocusProxy(renameBox);
+ renameBox->setFocus();
+ renameBox->show();
+ Q_ASSERT(view->d->renamingItem == 0L);
+ view->d->renamingItem = this;
+}
+#endif
+
+/*!
+ Compares this icon view item to \a i. Returns -1 if this item is
+ less than \a i, 0 if they are equal, and 1 if this icon view item
+ is greater than \a i.
+
+ The default implementation compares the item keys (key()) using
+ QString::localeAwareCompare(). A reimplementation may use
+ different values and a different comparison function. Here is a
+ reimplementation that uses plain Unicode comparison:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 3
+
+ \sa key() QString::localeAwareCompare() QString::compare()
+*/
+
+int Q3IconViewItem::compare(Q3IconViewItem *i) const
+{
+ return key().localeAwareCompare(i->key());
+}
+
+#ifndef QT_NO_TEXTEDIT
+/*!
+ This private function is called when the user pressed Return during
+ in-place renaming.
+*/
+
+void Q3IconViewItem::renameItem()
+{
+ if (!renameBox || !view)
+ return;
+
+ if (!view->d->wordWrapIconText) {
+ wordWrapDirty = true;
+ calcRect();
+ }
+ QRect r = itemRect;
+ setText(renameBox->text());
+ view->repaintContents(oldRect.x() - 1, oldRect.y() - 1, oldRect.width() + 2, oldRect.height() + 2);
+ view->repaintContents(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2);
+ removeRenameBox();
+
+ view->emitRenamed(this);
+}
+
+/*!
+ Cancels in-place renaming.
+*/
+
+void Q3IconViewItem::cancelRenameItem()
+{
+ if (!view)
+ return;
+
+ QRect r = itemRect;
+ calcRect();
+ view->repaintContents(oldRect.x() - 1, oldRect.y() - 1, oldRect.width() + 2, oldRect.height() + 2);
+ view->repaintContents(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2);
+
+ if (!renameBox)
+ return;
+
+ removeRenameBox();
+}
+
+/*!
+ Removes the editbox that is used for in-place renaming.
+*/
+
+void Q3IconViewItem::removeRenameBox()
+{
+ if (!renameBox || !view)
+ return;
+
+ bool resetFocus = view->viewport()->focusProxy() == renameBox;
+ renameBox->hide();
+ renameBox->deleteLater();
+ renameBox = 0;
+ if (resetFocus) {
+ view->viewport()->setFocusProxy(view);
+ view->setFocus();
+ }
+ Q_ASSERT(view->d->renamingItem == this);
+ view->d->renamingItem = 0L;
+}
+#endif
+
+/*!
+ This virtual function is responsible for calculating the
+ rectangles returned by rect(), textRect() and pixmapRect().
+ setRect(), setTextRect() and setPixmapRect() are provided mainly
+ for reimplementations of this function.
+
+ \a text_ is an internal parameter which defaults to an empty
+ string.
+*/
+
+void Q3IconViewItem::calcRect(const QString &text_)
+{
+ if (!view) // #####
+ return;
+
+ wordWrapDirty = true;
+ int pw = 0;
+ int ph = 0;
+
+#ifndef QT_NO_PICTURE
+ if (picture()) {
+ QRect br = picture()->boundingRect();
+ pw = br.width() + 2;
+ ph = br.height() + 2;
+ } else
+#endif
+ {
+ pw = (pixmap() ? pixmap() : unknown_icon)->width() + 2;
+ ph = (pixmap() ? pixmap() : unknown_icon)->height() + 2;
+ }
+
+ itemIconRect.setWidth(pw);
+ itemIconRect.setHeight(ph);
+
+ calcTmpText();
+
+ QString t = text_;
+ if (t.isEmpty()) {
+ if (view->d->wordWrapIconText)
+ t = itemText;
+ else
+ t = tmpText;
+ }
+
+ int tw = 0;
+ int th = 0;
+ // ##### TODO: fix font bearings!
+ QRect r;
+ if (view->d->wordWrapIconText) {
+ r = QRect(view->d->fm->boundingRect(0, 0, iconView()->maxItemWidth() -
+ (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 :
+ pixmapRect().width()),
+ 0xFFFFFFFF, Qt::AlignHCenter | Qt::WordBreak | Qt::BreakAnywhere, t));
+ r.setWidth(r.width() + 4);
+ } else {
+ r = QRect(0, 0, view->d->fm->width(t), view->d->fm->height());
+ r.setWidth(r.width() + 4);
+ }
+
+ if (r.width() > iconView()->maxItemWidth() -
+ (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 :
+ pixmapRect().width()))
+ r.setWidth(iconView()->maxItemWidth() - (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 :
+ pixmapRect().width()));
+
+ tw = r.width();
+ th = r.height();
+ if (tw < view->d->fm->width(QLatin1Char('X')))
+ tw = view->d->fm->width(QLatin1Char('X'));
+
+ itemTextRect.setWidth(tw);
+ itemTextRect.setHeight(th);
+
+ int w = 0;
+ int h = 0;
+ if (view->itemTextPos() == Q3IconView::Bottom) {
+ w = qMax(itemTextRect.width(), itemIconRect.width());
+ h = itemTextRect.height() + itemIconRect.height() + 1;
+
+ itemRect.setWidth(w);
+ itemRect.setHeight(h);
+
+ itemTextRect = QRect((width() - itemTextRect.width()) / 2, height() - itemTextRect.height(),
+ itemTextRect.width(), itemTextRect.height());
+ itemIconRect = QRect((width() - itemIconRect.width()) / 2, 0,
+ itemIconRect.width(), itemIconRect.height());
+ } else {
+ h = qMax(itemTextRect.height(), itemIconRect.height());
+ w = itemTextRect.width() + itemIconRect.width() + 1;
+
+ itemRect.setWidth(w);
+ itemRect.setHeight(h);
+
+ itemTextRect = QRect(width() - itemTextRect.width(), (height() - itemTextRect.height()) / 2,
+ itemTextRect.width(), itemTextRect.height());
+ itemIconRect = QRect(0, (height() - itemIconRect.height()) / 2,
+ itemIconRect.width(), itemIconRect.height());
+ }
+ if (view)
+ view->updateItemContainer(this);
+}
+
+/*!
+ Paints the item using the painter \a p and the color group \a cg.
+ If you want the item to be drawn with a different font or color,
+ reimplement this function, change the values of the color group or
+ the painter's font, and then call the Q3IconViewItem::paintItem()
+ with the changed values.
+*/
+
+void Q3IconViewItem::paintItem(QPainter *p, const QColorGroup &cg)
+{
+ if (!view)
+ return;
+
+ p->save();
+
+ if (isSelected()) {
+ p->setPen(cg.highlightedText());
+ } else {
+ p->setPen(cg.text());
+ }
+
+ calcTmpText();
+
+#ifndef QT_NO_PICTURE
+ if (picture()) {
+ QPicture *pic = picture();
+ if (isSelected()) {
+ p->fillRect(pixmapRect(false), QBrush(cg.highlight(), Qt::Dense4Pattern));
+ }
+ p->drawPicture(x()-pic->boundingRect().x(), y()-pic->boundingRect().y(), *pic);
+ if (isSelected()) {
+ p->fillRect(textRect(false), cg.highlight());
+ p->setPen(QPen(cg.highlightedText()));
+ } else if (view->d->itemTextBrush != QBrush(Qt::NoBrush))
+ p->fillRect(textRect(false), view->d->itemTextBrush);
+
+ int align = view->itemTextPos() == Q3IconView::Bottom ? Qt::AlignHCenter : Qt::AlignAuto;
+ if (view->d->wordWrapIconText)
+ align |= Qt::WordBreak | Qt::BreakAnywhere;
+ p->drawText(textRect(false), align, view->d->wordWrapIconText ? itemText : tmpText);
+ p->restore();
+ return;
+ }
+#endif
+ bool textOnBottom = (view->itemTextPos() == Q3IconView::Bottom);
+ int dim;
+ if (textOnBottom)
+ dim = (pixmap() ? pixmap() : unknown_icon)->width();
+ else
+ dim = (pixmap() ? pixmap() : unknown_icon)->height();
+ if (isSelected()) {
+ QPixmap *pix = pixmap() ? pixmap() : unknown_icon;
+ if (pix && !pix->isNull()) {
+ QPixmap *buffer = get_qiv_buffer_pixmap(pix->size());
+ QBitmap mask = view->mask(pix);
+
+ QPainter p2(buffer);
+ p2.fillRect(pix->rect(), Qt::white);
+ p2.drawPixmap(0, 0, *pix);
+ p2.end();
+
+ p2.begin(buffer);
+ p2.fillRect(pix->rect(), QBrush(cg.highlight(), Qt::Dense4Pattern));
+ p2.end();
+ buffer->setMask(mask);
+
+ QRect cr = pix->rect();
+ if (textOnBottom)
+ p->drawPixmap(x() + (width() - dim) / 2, y(), *buffer, 0, 0,
+ cr.width(), cr.height());
+ else
+ p->drawPixmap(x() , y() + (height() - dim) / 2, *buffer, 0, 0,
+ cr.width(), cr.height());
+ }
+ } else {
+ if (textOnBottom)
+ p->drawPixmap(x() + (width() - dim) / 2, y(),
+ *(pixmap() ? pixmap() : unknown_icon));
+ else
+ p->drawPixmap(x() , y() + (height() - dim) / 2,
+ *(pixmap() ? pixmap() : unknown_icon));
+ }
+
+ p->save();
+ if (isSelected()) {
+ p->fillRect(textRect(false), cg.highlight());
+ p->setPen(QPen(cg.highlightedText()));
+ } else if (view->d->itemTextBrush != QBrush(Qt::NoBrush))
+ p->fillRect(textRect(false), view->d->itemTextBrush);
+
+ int align = Qt::AlignHCenter;
+ if (view->d->wordWrapIconText)
+ align |= Qt::WordBreak | Qt::BreakAnywhere;
+ p->drawText(textRect(false), align,
+ view->d->wordWrapIconText ? itemText : tmpText);
+
+ p->restore();
+
+ p->restore();
+}
+
+/*!
+ Paints the focus rectangle of the item using the painter \a p and
+ the color group \a cg.
+*/
+
+void Q3IconViewItem::paintFocus(QPainter *p, const QColorGroup &cg)
+{
+ if (!view)
+ return;
+
+ QStyleOptionFocusRect opt;
+ opt.rect = textRect(false);
+ opt.palette = cg;
+ if (isSelected()) {
+ opt.state = QStyle::State_FocusAtBorder;
+ opt.backgroundColor = cg.highlight();
+ } else {
+ opt.state = QStyle::State_None;
+ opt.backgroundColor = cg.base();
+ }
+ view->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p);
+
+ if (this != view->d->currentItem) {
+ opt.rect = pixmapRect(false);
+ opt.backgroundColor = cg.base();
+ opt.state = QStyle::State_None;
+ view->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p);
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ \fn void Q3IconViewItem::dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst)
+
+ This function is called when something is dropped on the item. \a
+ e provides all the information about the drop. If the drag object
+ of the drop was a Q3IconDrag, \a lst contains the list of the
+ dropped items. You can get the data by calling
+ Q3IconDragItem::data() on each item. If the \a lst is empty, i.e.
+ the drag was not a Q3IconDrag, you must decode the data in \a e and
+ work with that.
+
+ The default implementation does nothing; subclasses may
+ reimplement this function.
+*/
+
+void Q3IconViewItem::dropped(QDropEvent *, const Q3ValueList<Q3IconDragItem> &)
+{
+}
+#endif
+
+/*!
+ This function is called when a drag enters the item's bounding
+ rectangle.
+
+ The default implementation does nothing; subclasses may
+ reimplement this function.
+*/
+
+void Q3IconViewItem::dragEntered()
+{
+}
+
+/*!
+ This function is called when a drag leaves the item's bounding
+ rectangle.
+
+ The default implementation does nothing; subclasses may
+ reimplement this function.
+*/
+
+void Q3IconViewItem::dragLeft()
+{
+}
+
+/*!
+ Sets the bounding rectangle of the whole item to \a r. This
+ function is provided for subclasses which reimplement calcRect(),
+ so that they can set the calculated rectangle. \e{Any other use is
+ discouraged.}
+
+ \sa calcRect() textRect() setTextRect() pixmapRect() setPixmapRect()
+*/
+
+void Q3IconViewItem::setItemRect(const QRect &r)
+{
+ itemRect = r;
+ checkRect();
+ if (view)
+ view->updateItemContainer(this);
+}
+
+/*!
+ Sets the bounding rectangle of the item's text to \a r. This
+ function is provided for subclasses which reimplement calcRect(),
+ so that they can set the calculated rectangle. \e{Any other use is
+ discouraged.}
+
+ \sa calcRect() textRect() setItemRect() setPixmapRect()
+*/
+
+void Q3IconViewItem::setTextRect(const QRect &r)
+{
+ itemTextRect = r;
+ if (view)
+ view->updateItemContainer(this);
+}
+
+/*!
+ Sets the bounding rectangle of the item's icon to \a r. This
+ function is provided for subclasses which reimplement calcRect(),
+ so that they can set the calculated rectangle. \e{Any other use is
+ discouraged.}
+
+ \sa calcRect() pixmapRect() setItemRect() setTextRect()
+*/
+
+void Q3IconViewItem::setPixmapRect(const QRect &r)
+{
+ itemIconRect = r;
+ if (view)
+ view->updateItemContainer(this);
+}
+
+/*!
+ \internal
+*/
+
+void Q3IconViewItem::calcTmpText()
+{
+ if (!view || view->d->wordWrapIconText || !wordWrapDirty)
+ return;
+ wordWrapDirty = false;
+
+ int w = iconView()->maxItemWidth() - (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 :
+ pixmapRect().width());
+ if (view->d->fm->width(itemText) < w) {
+ tmpText = itemText;
+ return;
+ }
+
+ tmpText = QLatin1String("...");
+ int i = 0;
+ while (view->d->fm->width(tmpText + itemText[i]) < w)
+ tmpText += itemText[i++];
+ tmpText.remove((uint)0, 3);
+ tmpText += QLatin1String("...");
+}
+
+/*! \internal */
+
+QString Q3IconViewItem::tempText() const
+{
+ return tmpText;
+}
+
+void Q3IconViewItem::checkRect()
+{
+ int x = itemRect.x();
+ int y = itemRect.y();
+ int w = itemRect.width();
+ int h = itemRect.height();
+
+ bool changed = false;
+ if (x < 0) {
+ x = 0;
+ changed = true;
+ }
+ if (y < 0) {
+ y = 0;
+ changed = true;
+ }
+
+ if (changed)
+ itemRect.setRect(x, y, w, h);
+}
+
+
+/*!
+ \class Q3IconView
+ \brief The Q3IconView class provides an area with movable labelled icons.
+
+ \compat
+
+ A Q3IconView can display and manage a grid or other 2D layout of
+ labelled icons. Each labelled icon is a Q3IconViewItem. Items
+ (Q3IconViewItems) can be added or deleted at any time; items can be
+ moved within the Q3IconView. Single or multiple items can be
+ selected. Items can be renamed in-place. Q3IconView also supports
+ \link #draganddrop drag and drop\endlink.
+
+ Each item contains a label string, a pixmap or picture (the icon
+ itself) and optionally a sort key. The sort key is used for
+ sorting the items and defaults to the label string. The label
+ string can be displayed below or to the right of the icon (see \l
+ ItemTextPos).
+
+ The simplest way to create a Q3IconView is to create a Q3IconView
+ object and create some Q3IconViewItems with the Q3IconView as their
+ parent, set the icon view's geometry and show it.
+ For example:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 4
+
+ The Q3IconViewItem call passes a pointer to the Q3IconView we wish to
+ populate, along with the label text and a QPixmap.
+
+ When an item is inserted the Q3IconView allocates a position for it.
+ Existing items are rearranged if autoArrange() is true. The
+ default arrangement is \l LeftToRight -- the Q3IconView fills up
+ the \e left-most column from top to bottom, then moves one column
+ \e right and fills that from top to bottom and so on. The
+ arrangement can be modified with any of the following approaches:
+ \list
+ \i Call setArrangement(), e.g. with \l TopToBottom which will fill
+ the \e top-most row from left to right, then moves one row \e down
+ and fills that row from left to right and so on.
+ \i Construct each Q3IconViewItem using a constructor which allows
+ you to specify which item the new one is to follow.
+ \i Call setSorting() or sort() to sort the items.
+ \endlist
+
+ The spacing between items is set with setSpacing(). Items can be
+ laid out using a fixed grid using setGridX() and setGridY(); by
+ default the Q3IconView calculates a grid dynamically. The position
+ of items' label text is set with setItemTextPos(). The text's
+ background can be set with setItemTextBackground(). The maximum
+ width of an item and of its text are set with setMaxItemWidth()
+ and setMaxItemTextLength(). The label text will be word-wrapped if
+ it is too long; this is controlled by setWordWrapIconText(). If
+ the label text is truncated, the user can still see the entire
+ text in a tool tip if they hover the mouse over the item. This is
+ controlled with setShowToolTips().
+
+ Items which are \link Q3IconViewItem::isSelectable()
+ selectable\endlink may be selected depending on the SelectionMode;
+ the default is \l Single. Because Q3IconView offers multiple
+ selection it must display keyboard focus and selection state
+ separately. Therefore there are functions to set the selection
+ state of an item (setSelected()) and to select which item displays
+ keyboard focus (setCurrentItem()). When multiple items may be
+ selected the icon view provides a rubberband, too.
+
+ When in-place renaming is enabled (it is disabled by default), the
+ user may change the item's label. They do this by selecting the item
+ (single clicking it or navigating to it with the arrow keys), then
+ single clicking it (or pressing F2), and entering their text. If no
+ key has been set with Q3IconViewItem::setKey() the new text will also
+ serve as the key. (See Q3IconViewItem::setRenameEnabled().)
+
+ You can control whether users can move items themselves with
+ setItemsMovable().
+
+ Because the internal structure used to store the icon view items is
+ linear, no iterator class is needed to iterate over all the items.
+ Instead we iterate by getting the first item from the \e{icon view}
+ and then each subsequent (\l Q3IconViewItem::nextItem()) from each
+ \e item in turn:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 5
+ Q3IconView also provides currentItem(). You can search for an item
+ using findItem() (searching by position or for label text) and
+ with findFirstVisibleItem() and findLastVisibleItem(). The number
+ of items is returned by count(). An item can be removed from an
+ icon view using takeItem(); to delete an item use \c delete. All
+ the items can be deleted with clear().
+
+ The Q3IconView emits a wide range of useful signals, including
+ selectionChanged(), currentChanged(), clicked(), moved() and
+ itemRenamed().
+
+ \target draganddrop
+ \section1 Drag and Drop
+
+ Q3IconView supports the drag and drop of items within the Q3IconView
+ itself. It also supports the drag and drop of items out of or into
+ the Q3IconView and drag and drop onto items themselves. The drag and
+ drop of items outside the Q3IconView can be achieved in a simple way
+ with basic functionality, or in a more sophisticated way which
+ provides more power and control.
+
+ The simple approach to dragging items out of the icon view is to
+ subclass Q3IconView and reimplement Q3IconView::dragObject().
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 6
+
+ In this example we create a Q3TextDrag object, (derived from
+ Q3DragObject), containing the item's label and return it as the drag
+ object. We could just as easily have created a Q3ImageDrag from the
+ item's pixmap and returned that instead.
+
+ Q3IconViews and their Q3IconViewItems can also be the targets of drag
+ and drops. To make the Q3IconView itself able to accept drops connect
+ to the dropped() signal. When a drop occurs this signal will be
+ emitted with a QDragEvent and a QLinkedList of Q3IconDragItems. To
+ make a Q3IconViewItem into a drop target subclass Q3IconViewItem and
+ reimplement Q3IconViewItem::acceptDrop() and
+ Q3IconViewItem::dropped().
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 7
+
+ If you want to use extended drag-and-drop or have drag shapes drawn
+ you must take a more sophisticated approach.
+
+ The first part is starting drags -- you should use a Q3IconDrag (or a
+ class derived from it) for the drag object. In dragObject() create the
+ drag object, populate it with Q3IconDragItems and return it. Normally
+ such a drag should offer each selected item's data. So in dragObject()
+ you should iterate over all the items, and create a Q3IconDragItem for
+ each selected item, and append these items with Q3IconDrag::append() to
+ the Q3IconDrag object. You can use Q3IconDragItem::setData() to set the
+ data of each item that should be dragged. If you want to offer the
+ data in additional mime-types, it's best to use a class derived from
+ Q3IconDrag, which implements additional encoding and decoding
+ functions.
+
+ When a drag enters the icon view, there is little to do. Simply
+ connect to the dropped() signal and reimplement
+ Q3IconViewItem::acceptDrop() and Q3IconViewItem::dropped(). If you've
+ used a Q3IconDrag (or a subclass of it) the second argument to the
+ dropped signal contains a QLinkedList of Q3IconDragItems -- you can
+ access their data by calling Q3IconDragItem::data() on each one.
+
+ For an example implementation of complex drag-and-drop look at the
+ fileiconview example (qt/examples/fileiconview).
+
+ \sa Q3IconViewItem::setDragEnabled(), Q3IconViewItem::setDropEnabled(),
+ Q3IconViewItem::acceptDrop(), Q3IconViewItem::dropped()
+*/
+
+/*! \enum Q3IconView::ResizeMode
+
+ This enum type is used to tell Q3IconView how it should treat the
+ positions of its icons when the widget is resized. The modes are:
+
+ \value Fixed The icons' positions are not changed.
+ \value Adjust The icons' positions are adjusted to be within the
+ new geometry, if possible.
+*/
+
+/*!
+ \enum Q3IconView::SelectionMode
+
+ This enumerated type is used by Q3IconView to indicate how it
+ reacts to selection by the user. It has four values:
+
+ \value Single When the user selects an item, any already-selected
+ item becomes unselected and the user cannot unselect the selected
+ item. This means that the user can never clear the selection. (The
+ application programmer can, using Q3IconView::clearSelection().)
+
+ \value Multi When the user selects an item, e.g. by navigating to
+ it with the keyboard arrow keys or by clicking it, the selection
+ status of that item is toggled and the other items are left alone.
+
+ \value Extended When the user selects an item the selection is
+ cleared and the new item selected. However, if the user presses
+ the Ctrl key when clicking on an item, the clicked item gets
+ toggled and all other items are left untouched. If the user
+ presses the Shift key while clicking on an item, all items between
+ the current item and the clicked item get selected or unselected,
+ depending on the state of the clicked item. Also, multiple items
+ can be selected by dragging the mouse while the left mouse button
+ stays pressed.
+
+ \value NoSelection Items cannot be selected.
+
+ To summarize: \c Single is a real single-selection icon view; \c
+ Multi a real multi-selection icon view; \c Extended is an icon
+ view in which users can select multiple items but usually want to
+ select either just one or a range of contiguous items; and \c
+ NoSelection mode is for an icon view where the user can look but
+ not touch.
+*/
+
+/*!
+ \enum Q3IconView::Arrangement
+
+ This enum type determines in which direction the items flow when
+ the view runs out of space.
+
+ \value LeftToRight Items which don't fit into the view go further
+ down (you get a vertical scroll bar)
+
+ \value TopToBottom Items which don't fit into the view go further
+ right (you get a horizontal scroll bar)
+*/
+
+/*!
+ \enum Q3IconView::ItemTextPos
+
+ This enum type specifies the position of the item text in relation
+ to the icon.
+
+ \value Bottom The text is drawn below the icon.
+ \value Right The text is drawn to the right of the icon.
+*/
+
+/*!
+ \fn void Q3IconView::dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst)
+
+ This signal is emitted when a drop event occurs in the viewport
+ (but not on any icon) which the icon view itself can't handle.
+
+ \a e provides all the information about the drop. If the drag
+ object of the drop was a Q3IconDrag, \a lst contains the list of
+ the dropped items. You can get the data using
+ Q3IconDragItem::data() on each item. If the \a lst is empty, i.e.
+ the drag was not a Q3IconDrag, you have to decode the data in \a e
+ and work with that.
+
+ Note Q3IconViewItems may be drop targets; if a drop event occurs on
+ an item the item handles the drop.
+*/
+
+/*!
+ \fn void Q3IconView::moved()
+
+ This signal is emitted after successfully dropping one (or more)
+ items of the icon view. If the items should be removed, it's best
+ to do so in a slot connected to this signal.
+*/
+
+/*!
+ \fn void Q3IconView::doubleClicked(Q3IconViewItem * item)
+
+ This signal is emitted when the user double-clicks on \a item.
+*/
+
+/*!
+ \fn void Q3IconView::returnPressed (Q3IconViewItem * item)
+
+ This signal is emitted if the user presses the Return or Enter
+ key. \a item is the currentItem() at the time of the keypress.
+*/
+
+/*!
+ \fn void Q3IconView::selectionChanged()
+
+ This signal is emitted when the selection has been changed. It's
+ emitted in each selection mode.
+*/
+
+/*!
+ \fn void Q3IconView::selectionChanged(Q3IconViewItem *item)
+ \overload
+
+ This signal is emitted when the selection changes. \a item is the
+ newly selected item. This signal is emitted only in single
+ selection mode.
+*/
+
+/*!
+ \fn void Q3IconView::currentChanged(Q3IconViewItem *item)
+
+ This signal is emitted when a new item becomes current. \a item is
+ the new current item (or 0 if no item is now current).
+
+ \sa currentItem()
+*/
+
+/*!
+ \fn void Q3IconView::onItem(Q3IconViewItem *item)
+
+ This signal is emitted when the user moves the mouse cursor onto
+ an \a item, similar to the QWidget::enterEvent() function.
+*/
+
+// ### bug here - enter/leave event aren't considered. move the mouse
+// out of the window and back in, to the same item.
+
+/*!
+ \fn void Q3IconView::onViewport()
+
+ This signal is emitted when the user moves the mouse cursor from
+ an item to an empty part of the icon view.
+
+ \sa onItem()
+*/
+
+/*!
+ \fn void Q3IconView::itemRenamed (Q3IconViewItem * item)
+ \overload
+
+ This signal is emitted when \a item has been renamed, usually by
+ in-place renaming.
+
+ \sa Q3IconViewItem::setRenameEnabled() Q3IconViewItem::rename()
+*/
+
+/*!
+ \fn void Q3IconView::itemRenamed (Q3IconViewItem * item, const QString &name)
+
+ This signal is emitted when \a item has been renamed to \a name,
+ usually by in-place renaming.
+
+ \sa Q3IconViewItem::setRenameEnabled() Q3IconViewItem::rename()
+*/
+
+/*!
+ \fn void Q3IconView::rightButtonClicked (Q3IconViewItem * item, const QPoint & pos)
+
+ This signal is emitted when the user clicks the right mouse
+ button. If \a item is non-null, the cursor is on \a item. If \a
+ item is null, the mouse cursor isn't on any item.
+
+ \a pos is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()). (If the click's
+ press and release differ by a pixel or two, \a pos is the
+ position at release time.)
+
+ \sa rightButtonPressed() mouseButtonClicked() clicked()
+*/
+
+/*!
+ \fn void Q3IconView::contextMenuRequested(Q3IconViewItem *item, const QPoint & pos)
+
+ This signal is emitted when the user invokes a context menu with
+ the right mouse button or with special system keys, with \a item
+ being the item under the mouse cursor or the current item,
+ respectively.
+
+ \a pos is the position for the context menu in the global
+ coordinate system.
+*/
+
+/*!
+ \fn void Q3IconView::mouseButtonPressed(int button, Q3IconViewItem *item, const QPoint &pos)
+
+ This signal is emitted when the user presses mouse button \a
+ button. If \a item is non-null, the cursor is on \a item. If \a
+ item is null, the mouse cursor isn't on any item.
+
+ \a pos is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()).
+
+ \sa rightButtonClicked() mouseButtonClicked() pressed()
+*/
+
+/*!
+ \fn void Q3IconView::mouseButtonClicked (int button, Q3IconViewItem * item, const QPoint & pos)
+
+ This signal is emitted when the user clicks mouse button \a
+ button. If \a item is non-null, the cursor is on \a item. If \a
+ item is null, the mouse cursor isn't on any item.
+
+ \a pos is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()). (If the click's
+ press and release differ by a pixel or two, \a pos is the
+ position at release time.)
+
+ \sa mouseButtonPressed() rightButtonClicked() clicked()
+*/
+
+/*!
+ \fn void Q3IconView::clicked (Q3IconViewItem * item, const QPoint & pos)
+ \overload
+
+ This signal is emitted when the user clicks any mouse button on an
+ icon view item. \a item is a pointer to the item that has been
+ clicked.
+
+ \a pos is the position of the mouse cursor in the global coordinate
+ system (QMouseEvent::globalPos()). (If the click's press and release
+ differ by a pixel or two, \a pos is the position at release time.)
+
+ \sa mouseButtonClicked() rightButtonClicked() pressed()
+*/
+
+/*!
+ \fn void Q3IconView::pressed (Q3IconViewItem * item, const QPoint & pos)
+ \overload
+
+ This signal is emitted when the user presses any mouse button. If
+ \a item is non-null, the cursor is on \a item. If \a item is null,
+ the mouse cursor isn't on any item.
+
+ \a pos is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()). (If the click's
+ press and release differ by a pixel or two, \a pos is the
+ position at release time.)
+
+ \sa mouseButtonPressed() rightButtonPressed() clicked()
+*/
+
+/*!
+ \fn void Q3IconView::clicked (Q3IconViewItem * item)
+
+ This signal is emitted when the user clicks any mouse button. If
+ \a item is non-null, the cursor is on \a item. If \a item is null,
+ the mouse cursor isn't on any item.
+
+ \sa mouseButtonClicked() rightButtonClicked() pressed()
+*/
+
+/*!
+ \fn void Q3IconView::pressed (Q3IconViewItem * item)
+
+ This signal is emitted when the user presses any mouse button. If
+ \a item is non-null, the cursor is on \a item. If \a item is null,
+ the mouse cursor isn't on any item.
+
+ \sa mouseButtonPressed() rightButtonPressed() clicked()
+*/
+
+/*!
+ \fn void Q3IconView::rightButtonPressed(Q3IconViewItem * item, const QPoint & pos)
+
+ This signal is emitted when the user presses the right mouse
+ button. If \a item is non-null, the cursor is on \a item. If \a
+ item is null, the mouse cursor isn't on any item.
+
+ \a pos is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()).
+*/
+
+/*!
+ Constructs an empty icon view called \a name, with parent \a
+ parent and using the widget flags \a f.
+*/
+
+Q3IconView::Q3IconView(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase | f)
+{
+ if (!unknown_icon) {
+ unknown_icon = new QPixmap((const char **)unknown_xpm);
+ qiv_pixmaps()->append(unknown_icon);
+ }
+
+ d = new Q3IconViewPrivate;
+ d->dragging = false;
+ d->firstItem = 0;
+ d->lastItem = 0;
+ d->count = 0;
+ d->mousePressed = false;
+ d->selectionMode = Single;
+ d->currentItem = 0;
+ d->highlightedItem = 0;
+ d->rubber = 0;
+ d->scrollTimer = 0;
+ d->startDragItem = 0;
+ d->tmpCurrentItem = 0;
+ d->rastX = d->rastY = -1;
+ d->spacing = 5;
+ d->cleared = false;
+ d->arrangement = LeftToRight;
+ d->resizeMode = Fixed;
+ d->dropped = false;
+ d->adjustTimer = new QTimer(this, "iconview adjust timer");
+ d->isIconDrag = false;
+ d->inMenuMode = false;
+#ifndef QT_NO_DRAGANDDROP
+ d->iconDragData.clear();
+#endif
+ d->numDragItems = 0;
+ d->updateTimer = new QTimer(this, "iconview update timer");
+ d->cachedW = d->cachedH = 0;
+ d->maxItemWidth = 100;
+ d->maxItemTextLength = 255;
+ d->inputTimer = new QTimer(this, "iconview input timer");
+ d->currInputString.clear();
+ d->dirty = false;
+ d->rearrangeEnabled = true;
+ d->itemTextPos = Bottom;
+ d->reorderItemsWhenInsert = true;
+#ifndef QT_NO_CURSOR
+ d->oldCursor = Qt::ArrowCursor;
+#endif
+ d->resortItemsWhenInsert = false;
+ d->sortDirection = true;
+ d->wordWrapIconText = true;
+ d->cachedContentsX = d->cachedContentsY = -1;
+ d->clearing = false;
+ d->fullRedrawTimer = new QTimer(this, "iconview full redraw timer");
+ d->itemTextBrush = Qt::NoBrush;
+ d->drawAllBack = true;
+ d->fm = new QFontMetrics(font());
+ d->minLeftBearing = d->fm->minLeftBearing();
+ d->minRightBearing = d->fm->minRightBearing();
+ d->firstContainer = d->lastContainer = 0;
+ d->containerUpdateLocked = false;
+ d->firstSizeHint = false;
+ d->selectAnchor = 0;
+ d->renamingItem = 0;
+ d->drawActiveSelection = true;
+ d->drawDragShapes = false;
+
+ connect(d->adjustTimer, SIGNAL(timeout()),
+ this, SLOT(adjustItems()));
+ connect(d->updateTimer, SIGNAL(timeout()),
+ this, SLOT(slotUpdate()));
+ connect(d->fullRedrawTimer, SIGNAL(timeout()),
+ this, SLOT(updateContents()));
+ connect(this, SIGNAL(contentsMoving(int,int)),
+ this, SLOT(movedContents(int,int)));
+
+ setAcceptDrops(true);
+ viewport()->setAcceptDrops(true);
+
+ setMouseTracking(true);
+ viewport()->setMouseTracking(true);
+
+ viewport()->setBackgroundRole(QPalette::Base);
+ viewport()->setFocusProxy(this);
+ viewport()->setFocusPolicy(Qt::WheelFocus);
+ setFocusPolicy(Qt::WheelFocus);
+
+ d->showTips = true;
+}
+
+/*!
+ \reimp
+*/
+void Q3IconView::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange) {
+ *d->fm = QFontMetrics(font());
+ d->minLeftBearing = d->fm->minLeftBearing();
+ d->minRightBearing = d->fm->minRightBearing();
+
+ Q3IconViewItem *item = d->firstItem;
+ for (; item; item = item->next) {
+ item->wordWrapDirty = true;
+ item->calcRect();
+ }
+
+#if !defined(Q_WS_X11)
+ delete qiv_selection;
+ qiv_selection = 0;
+#endif
+ } else if(ev->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow() && d->scrollTimer)
+ d->scrollTimer->stop();
+ if(isVisible() && !palette().isEqual(QPalette::Active, QPalette::Inactive))
+ repaintSelectedItems();
+ }
+
+ Q3ScrollView::changeEvent(ev);
+
+ if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) {
+ *d->fm = QFontMetrics(font());
+ d->minLeftBearing = d->fm->minLeftBearing();
+ d->minRightBearing = d->fm->minRightBearing();
+
+ Q3IconViewItem *item = d->firstItem;
+ for (; item; item = item->next) {
+ item->wordWrapDirty = true;
+ item->calcRect();
+ }
+ }
+}
+
+/*!
+ Destroys the icon view and deletes all items.
+*/
+
+Q3IconView::~Q3IconView()
+{
+ Q3IconViewItem *tmp, *item = d->firstItem;
+ d->clearing = true;
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc;
+ while (c) {
+ tmpc = c->n;
+ delete c;
+ c = tmpc;
+ }
+ while (item) {
+ tmp = item->next;
+ delete item;
+ item = tmp;
+ }
+ delete d->fm;
+ d->fm = 0;
+ delete d;
+}
+
+/*!
+ Inserts the icon view item \a item after \a after. If \a after is
+ 0, \a item is appended after the last item.
+
+ \e{You should never need to call this function.} Instead create
+ Q3IconViewItem's and associate them with your icon view like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 8
+*/
+
+void Q3IconView::insertItem(Q3IconViewItem *item, Q3IconViewItem *after)
+{
+ if (!item)
+ return;
+
+ if (d->firstItem == item || item->prev || item->next)
+ return;
+
+ if (!item->view)
+ item->view = this;
+
+ if (!d->firstItem) {
+ d->firstItem = d->lastItem = item;
+ item->prev = 0;
+ item->next = 0;
+ } else {
+ if (!after || after == d->lastItem) {
+ d->lastItem->next = item;
+ item->prev = d->lastItem;
+ item->next = 0;
+ d->lastItem = item;
+ } else {
+ Q3IconViewItem *i = d->firstItem;
+ while (i != after)
+ i = i->next;
+
+ if (i) {
+ Q3IconViewItem *next = i->next;
+ item->next = next;
+ item->prev = i;
+ i->next = item;
+ next->prev = item;
+ }
+ }
+ }
+
+ if (isVisible()) {
+ if (d->reorderItemsWhenInsert) {
+ if (d->updateTimer->isActive())
+ d->updateTimer->stop();
+ d->fullRedrawTimer->stop();
+ // #### uncomment this ASA insertInGrid uses cached values and is efficient
+ //insertInGrid(item);
+
+ d->cachedW = qMax(d->cachedW, item->x() + item->width());
+ d->cachedH= qMax(d->cachedH, item->y() + item->height());
+
+ d->updateTimer->start(0, true);
+ } else {
+ insertInGrid(item);
+
+ viewport()->update(item->x() - contentsX(),
+ item->y() - contentsY(),
+ item->width(), item->height());
+ }
+ } else if (!autoArrange()) {
+ item->dirty = false;
+ }
+
+ d->count++;
+ d->dirty = true;
+}
+
+/*!
+ This slot is used for a slightly-delayed update.
+
+ The icon view is not redrawn immediately after inserting a new item
+ but after a very small delay using a QTimer. This means that when
+ many items are inserted in a loop the icon view is probably redrawn
+ only once at the end of the loop. This makes the insertions both
+ flicker-free and faster.
+*/
+
+void Q3IconView::slotUpdate()
+{
+ d->updateTimer->stop();
+ d->fullRedrawTimer->stop();
+
+ if (!d->firstItem || !d->lastItem)
+ return;
+
+ // #### remove that ASA insertInGrid uses cached values and is efficient
+ if (d->resortItemsWhenInsert)
+ sort(d->sortDirection);
+ else {
+ int y = d->spacing;
+ Q3IconViewItem *item = d->firstItem;
+ int w = 0, h = 0;
+ while (item) {
+ bool changed;
+ Q3IconViewItem *next = makeRowLayout(item, y, changed);
+ if (!next || !next->next)
+ break;
+
+ if(!QApplication::reverseLayout())
+ item = next;
+ w = qMax(w, item->x() + item->width());
+ h = qMax(h, item->y() + item->height());
+ item = next;
+ if (d->arrangement == LeftToRight)
+ h = qMax(h, y);
+
+ item = item->next;
+ }
+
+ if (d->lastItem && d->arrangement == TopToBottom) {
+ item = d->lastItem;
+ int x = item->x();
+ while (item && item->x() >= x) {
+ w = qMax(w, item->x() + item->width());
+ h = qMax(h, item->y() + item->height());
+ item = item->prev;
+ }
+ }
+
+ w = qMax(qMax(d->cachedW, w), d->lastItem->x() + d->lastItem->width());
+ h = qMax(qMax(d->cachedH, h), d->lastItem->y() + d->lastItem->height());
+
+ if (d->arrangement == TopToBottom)
+ w += d->spacing;
+ else
+ h += d->spacing;
+ viewport()->setUpdatesEnabled(false);
+ resizeContents(w, h);
+ viewport()->setUpdatesEnabled(true);
+ viewport()->repaint();
+ }
+
+ int cx = d->cachedContentsX == -1 ? contentsX() : d->cachedContentsX;
+ int cy = d->cachedContentsY == -1 ? contentsY() : d->cachedContentsY;
+
+ if (cx != contentsX() || cy != contentsY())
+ setContentsPos(cx, cy);
+
+ d->cachedContentsX = d->cachedContentsY = -1;
+ d->cachedW = d->cachedH = 0;
+}
+
+/*!
+ Takes the icon view item \a item out of the icon view and causes
+ an update of the screen display. The item is not deleted. You
+ should normally not need to call this function because
+ Q3IconViewItem::~Q3IconViewItem() calls it. The normal way to delete
+ an item is to delete it.
+*/
+
+void Q3IconView::takeItem(Q3IconViewItem *item)
+{
+ if (!item)
+ return;
+
+ if (item->d->container1)
+ item->d->container1->items.removeAll(item);
+ if (item->d->container2)
+ item->d->container2->items.removeAll(item);
+ item->d->container2 = 0;
+ item->d->container1 = 0;
+
+ bool block = signalsBlocked();
+ blockSignals(d->clearing);
+
+ QRect r = item->rect();
+
+ if (d->currentItem == item) {
+ if (item->prev) {
+ d->currentItem = item->prev;
+ emit currentChanged(d->currentItem);
+ repaintItem(d->currentItem);
+ } else if (item->next) {
+ d->currentItem = item->next;
+ emit currentChanged(d->currentItem);
+ repaintItem(d->currentItem);
+ } else {
+ d->currentItem = 0;
+ emit currentChanged(d->currentItem);
+ }
+ }
+ if (item->isSelected()) {
+ item->selected = false;
+ emit selectionChanged();
+ }
+
+ if (item == d->firstItem) {
+ d->firstItem = d->firstItem->next;
+ if (d->firstItem)
+ d->firstItem->prev = 0;
+ } else if (item == d->lastItem) {
+ d->lastItem = d->lastItem->prev;
+ if (d->lastItem)
+ d->lastItem->next = 0;
+ } else {
+ Q3IconViewItem *i = item;
+ if (i) {
+ if (i->prev)
+ i->prev->next = i->next;
+ if (i->next)
+ i->next->prev = i->prev;
+ }
+ }
+
+ if (d->selectAnchor == item)
+ d->selectAnchor = d->currentItem;
+
+ if (!d->clearing)
+ repaintContents(r.x(), r.y(), r.width(), r.height());
+
+ item->view = 0;
+ item->prev = 0;
+ item->next = 0;
+ d->count--;
+
+ blockSignals(block);
+}
+
+/*!
+ Returns the index of \a item, or -1 if \a item doesn't exist in
+ this icon view.
+*/
+
+int Q3IconView::index(const Q3IconViewItem *item) const
+{
+ if (!item)
+ return -1;
+
+ if (item == d->firstItem)
+ return 0;
+ else if (item == d->lastItem)
+ return d->count - 1;
+ else {
+ Q3IconViewItem *i = d->firstItem;
+ int j = 0;
+ while (i && i != item) {
+ i = i->next;
+ ++j;
+ }
+
+ return i ? j : -1;
+ }
+}
+
+/*!
+ Returns a pointer to the first item of the icon view, or 0 if
+ there are no items in the icon view.
+
+ \sa lastItem() currentItem()
+*/
+
+Q3IconViewItem *Q3IconView::firstItem() const
+{
+ return d->firstItem;
+}
+
+/*!
+ Returns a pointer to the last item of the icon view, or 0 if there
+ are no items in the icon view.
+
+ \sa firstItem() currentItem()
+*/
+
+Q3IconViewItem *Q3IconView::lastItem() const
+{
+ return d->lastItem;
+}
+
+/*!
+ Returns a pointer to the current item of the icon view, or 0 if no
+ item is current.
+
+ \sa setCurrentItem() firstItem() lastItem()
+*/
+
+Q3IconViewItem *Q3IconView::currentItem() const
+{
+ return d->currentItem;
+}
+
+/*!
+ \reimp
+*/
+QVariant Q3IconView::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ if (query == Qt::ImMicroFocus) {
+ return d->currentItem ? d->currentItem->rect() : QRect();
+ }
+ return QWidget::inputMethodQuery(query);
+}
+
+/*!
+ Makes \a item the new current item of the icon view.
+*/
+
+void Q3IconView::setCurrentItem(Q3IconViewItem *item)
+{
+ if (!item || item == d->currentItem)
+ return;
+
+ Q3IconViewItem *old = d->currentItem;
+ d->currentItem = item;
+ emit currentChanged(d->currentItem);
+ if (d->selectionMode == Single) {
+ bool changed = false;
+ if (old && old->selected) {
+ old->selected = false;
+ changed = true;
+ }
+ if (item && !item->selected && item->isSelectable() && d->selectionMode != NoSelection) {
+ item->selected = true;
+ changed = true;
+ emit selectionChanged(item);
+ }
+ if (changed)
+ emit selectionChanged();
+ }
+
+ if (old)
+ repaintItem(old);
+ repaintItem(d->currentItem);
+}
+
+/*!
+ Selects or unselects \a item depending on \a s, and may also
+ unselect other items, depending on Q3IconView::selectionMode() and
+ \a cb.
+
+ If \a s is false, \a item is unselected.
+
+ If \a s is true and Q3IconView::selectionMode() is \l Single, \a
+ item is selected, and the item which was selected is unselected.
+
+ If \a s is true and Q3IconView::selectionMode() is \l Extended, \a
+ item is selected. If \a cb is true, the selection state of the
+ icon view's other items is left unchanged. If \a cb is false (the
+ default) all other items are unselected.
+
+ If \a s is true and Q3IconView::selectionMode() is \l Multi \a item
+ is selected.
+
+ Note that \a cb is used only if Q3IconView::selectionMode() is \l
+ Extended. \a cb defaults to false.
+
+ All items whose selection status is changed repaint themselves.
+*/
+
+void Q3IconView::setSelected(Q3IconViewItem *item, bool s, bool cb)
+{
+ if (!item)
+ return;
+ item->setSelected(s, cb);
+}
+
+/*!
+ \property Q3IconView::count
+ \brief the number of items in the icon view
+*/
+
+uint Q3IconView::count() const
+{
+ return d->count;
+}
+
+/*!
+ Performs autoscrolling when selecting multiple icons with the
+ rubber band.
+*/
+
+void Q3IconView::doAutoScroll()
+{
+ QRect oldRubber = QRect(*d->rubber);
+
+ QPoint vp = viewport()->mapFromGlobal(QCursor::pos());
+ QPoint pos = viewportToContents(vp);
+
+ if (pos == d->rubber->bottomRight())
+ return;
+
+ d->rubber->setRight(pos.x());
+ d->rubber->setBottom(pos.y());
+
+ int minx = contentsWidth(), miny = contentsHeight();
+ int maxx = 0, maxy = 0;
+ bool changed = false;
+ bool block = signalsBlocked();
+
+ QRect rr;
+ QRegion region(0, 0, visibleWidth(), visibleHeight());
+
+ blockSignals(true);
+ viewport()->setUpdatesEnabled(false);
+ bool alreadyIntersected = false;
+ QRect nr = d->rubber->normalized();
+ QRect rubberUnion = nr.united(oldRubber.normalized());
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer;
+ for (; c; c = c->n) {
+ if (c->rect.intersects(rubberUnion)) {
+ alreadyIntersected = true;
+ for (int i = 0; i < c->items.size(); ++i) {
+ Q3IconViewItem *item = c->items.at(i);
+ if (d->selectedItems.contains(item))
+ continue;
+ if (!item->intersects(nr)) {
+ if (item->isSelected()) {
+ item->setSelected(false);
+ changed = true;
+ rr = rr.united(item->rect());
+ }
+ } else if (item->intersects(nr)) {
+ if (!item->isSelected() && item->isSelectable()) {
+ item->setSelected(true, true);
+ changed = true;
+ rr = rr.united(item->rect());
+ } else {
+ region = region.subtracted(QRect(contentsToViewport(item->pos()),
+ item->size()));
+ }
+
+ minx = qMin(minx, item->x() - 1);
+ miny = qMin(miny, item->y() - 1);
+ maxx = qMax(maxx, item->x() + item->width() + 1);
+ maxy = qMax(maxy, item->y() + item->height() + 1);
+ }
+ }
+ } else {
+ if (alreadyIntersected)
+ break;
+ }
+ }
+ viewport()->setUpdatesEnabled(true);
+ blockSignals(block);
+
+ QRect r = *d->rubber;
+ *d->rubber = oldRubber;
+ d->dragging = false;
+ *d->rubber = r;
+ if (changed) {
+ d->drawAllBack = false;
+ d->clipRegion = region;
+ repaintContents(rr);
+ d->drawAllBack = true;
+ }
+ ensureVisible(pos.x(), pos.y());
+ d->dragging = true;
+
+ if (changed) {
+ emit selectionChanged();
+ if (d->selectionMode == Single)
+ emit selectionChanged(d->currentItem);
+ }
+
+ if (!QRect(50, 50, viewport()->width()-100, viewport()->height()-100).contains(vp) &&
+ !d->scrollTimer) {
+ d->scrollTimer = new QTimer(this);
+
+ connect(d->scrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+ d->scrollTimer->start(100, false);
+ } else if (QRect(50, 50, viewport()->width()-100, viewport()->height()-100).contains(vp) &&
+ d->scrollTimer) {
+ disconnect(d->scrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+ d->scrollTimer->stop();
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ }
+
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ if (d->dragging && d->rubber)
+ drawRubber(p);
+
+ QRect r = QRect(cx, cy, cw, ch);
+
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer;
+ QRegion remaining(QRect(cx, cy, cw, ch));
+ bool alreadyIntersected = false;
+ while (c) {
+ if (c->rect.intersects(r)) {
+ p->save();
+ p->resetXForm();
+ QRect r2 = c->rect;
+ r2 = r2.intersected(r);
+ QRect r3(contentsToViewport(QPoint(r2.x(), r2.y())), QSize(r2.width(), r2.height()));
+ if (d->drawAllBack) {
+ p->setClipRect(r3);
+ } else {
+ QRegion reg = d->clipRegion.intersected(r3);
+ p->setClipRegion(reg);
+ }
+ drawBackground(p, r3);
+ remaining = remaining.subtracted(r3);
+ p->restore();
+
+ QPalette pal = palette();
+ d->drawActiveSelection = hasFocus() || d->inMenuMode
+ || !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this);
+ if (!d->drawActiveSelection)
+ pal.setCurrentColorGroup(QPalette::Inactive);
+
+ // clip items to the container rect by default... this
+ // prevents icons with alpha channels from being painted
+ // twice when they are in 2 containers
+ //
+ // NOTE: the item could override this cliprect in its
+ // paintItem() implementation, which makes this useless
+ p->setClipRect(r2);
+ for (int i = 0; i < c->items.size(); ++i) {
+ Q3IconViewItem *item = c->items.at(i);
+ if (item->rect().intersects(r) && !item->dirty) {
+ p->save();
+ p->setFont(font());
+ item->paintItem(p, pal);
+ p->restore();
+ }
+ }
+ alreadyIntersected = true;
+ } else {
+ if (alreadyIntersected)
+ break;
+ }
+ c = c->n;
+ }
+
+ if (!remaining.isEmpty()) {
+ p->save();
+ p->resetXForm();
+ if (d->drawAllBack) {
+ p->setClipRegion(remaining);
+ } else {
+ remaining = d->clipRegion.intersected(remaining);
+ p->setClipRegion(remaining);
+ }
+ drawBackground(p, remaining.boundingRect());
+ p->restore();
+ }
+
+ if ((hasFocus() || viewport()->hasFocus()) && d->currentItem &&
+ d->currentItem->rect().intersects(r)) {
+ d->currentItem->paintFocus(p, palette());
+ }
+
+ if (d->dragging && d->rubber)
+ drawRubber(p);
+}
+
+/*!
+ \overload
+
+ Arranges all the items in the grid specified by gridX() and gridY().
+
+ Even if sorting() is enabled, the items are not sorted by this
+ function. If you want to sort or rearrange the items, use
+ iconview->sort(iconview->sortDirection()).
+
+ If \a update is true (the default), the viewport is repainted as
+ well.
+
+ \sa Q3IconView::setGridX(), Q3IconView::setGridY(), Q3IconView::sort()
+*/
+
+void Q3IconView::arrangeItemsInGrid(bool update)
+{
+ if (!d->firstItem || !d->lastItem)
+ return;
+
+ d->containerUpdateLocked = true;
+
+ int w = 0, h = 0, y = d->spacing;
+
+ Q3IconViewItem *item = d->firstItem;
+ bool changedLayout = false;
+ while (item) {
+ bool changed;
+ Q3IconViewItem *next = makeRowLayout(item, y, changed);
+ changedLayout = changed || changedLayout;
+ if(!QApplication::reverseLayout())
+ item = next;
+ w = qMax(w, item->x() + item->width());
+ h = qMax(h, item->y() + item->height());
+ item = next;
+ if (d->arrangement == LeftToRight)
+ h = qMax(h, y);
+
+ if (!item || !item->next)
+ break;
+
+ item = item->next;
+ }
+
+ if (d->lastItem && d->arrangement == TopToBottom) {
+ item = d->lastItem;
+ int x = item->x();
+ while (item && item->x() >= x) {
+ w = qMax(w, item->x() + item->width());
+ h = qMax(h, item->y() + item->height());
+ item = item->prev;
+ }
+ }
+ d->containerUpdateLocked = false;
+
+ w = qMax(qMax(d->cachedW, w), d->lastItem->x() + d->lastItem->width());
+ h = qMax(qMax(d->cachedH, h), d->lastItem->y() + d->lastItem->height());
+
+ if (d->arrangement == TopToBottom)
+ w += d->spacing;
+ else
+ h += d->spacing;
+
+ bool ue = updatesEnabled();
+ if (ue)
+ viewport()->setUpdatesEnabled(false);
+ int vw = visibleWidth();
+ int vh = visibleHeight();
+ resizeContents(w, h);
+ bool doAgain = false;
+ if (d->arrangement == LeftToRight)
+ doAgain = visibleWidth() != vw;
+ if (d->arrangement == TopToBottom)
+ doAgain = visibleHeight() != vh;
+ if (doAgain) // in the case that the visibleExtend changed because of the resizeContents (scroll bar show/hide), redo layout again
+ arrangeItemsInGrid(false);
+ if (ue)
+ viewport()->setUpdatesEnabled(true);
+ d->dirty = !isVisible();
+ rebuildContainers();
+ if (update && (!optimize_layout || changedLayout))
+ repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height());
+}
+
+/*!
+ This variant uses \a grid instead of (gridX(), gridY()). If \a
+ grid is invalid (see QSize::isValid()), arrangeItemsInGrid()
+ calculates a valid grid itself and uses that.
+
+ If \a update is true (the default) the viewport is repainted.
+*/
+
+void Q3IconView::arrangeItemsInGrid(const QSize &grid, bool update)
+{
+ d->containerUpdateLocked = true;
+ QSize grid_(grid);
+ if (!grid_.isValid()) {
+ int w = 0, h = 0;
+ Q3IconViewItem *item = d->firstItem;
+ for (; item; item = item->next) {
+ w = qMax(w, item->width());
+ h = qMax(h, item->height());
+ }
+
+ grid_ = QSize(qMax(d->rastX + d->spacing, w),
+ qMax(d->rastY + d->spacing, h));
+ }
+
+ int w = 0, h = 0;
+ Q3IconViewItem *item = d->firstItem;
+ for (; item; item = item->next) {
+ int nx = item->x() / grid_.width();
+ int ny = item->y() / grid_.height();
+ item->move(nx * grid_.width(),
+ ny * grid_.height());
+ w = qMax(w, item->x() + item->width());
+ h = qMax(h, item->y() + item->height());
+ item->dirty = false;
+ }
+ d->containerUpdateLocked = false;
+
+ resizeContents(w, h);
+ rebuildContainers();
+ if (update)
+ repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height());
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::setContentsPos(int x, int y)
+{
+ if (d->updateTimer->isActive()) {
+ d->cachedContentsX = x;
+ d->cachedContentsY = y;
+ } else {
+ d->cachedContentsY = d->cachedContentsX = -1;
+ Q3ScrollView::setContentsPos(x, y);
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::showEvent(QShowEvent *)
+{
+ if (d->dirty) {
+ resizeContents(qMax(contentsWidth(), viewport()->width()),
+ qMax(contentsHeight(), viewport()->height()));
+ if (d->resortItemsWhenInsert)
+ sort(d->sortDirection);
+ if (autoArrange())
+ arrangeItemsInGrid(false);
+ }
+ Q3ScrollView::show();
+}
+
+/*!
+ \property Q3IconView::selectionMode
+ \brief the selection mode of the icon view
+
+ This can be \l Single (the default), \l Extended, \l Multi or \l
+ NoSelection.
+*/
+
+void Q3IconView::setSelectionMode(SelectionMode m)
+{
+ d->selectionMode = m;
+}
+
+Q3IconView::SelectionMode Q3IconView::selectionMode() const
+{
+ return d->selectionMode;
+}
+
+/*!
+ Returns a pointer to the item that contains point \a pos, which is
+ given in contents coordinates, or 0 if no item contains point \a
+ pos.
+*/
+
+Q3IconViewItem *Q3IconView::findItem(const QPoint &pos) const
+{
+ if (!d->firstItem)
+ return 0;
+
+ Q3IconViewPrivate::ItemContainer *c = d->lastContainer;
+ for (; c; c = c->p) {
+ if (c->rect.contains(pos))
+ for (int i = c->items.size()-1; i >= 0; --i)
+ if (c->items.at(i)->contains(pos))
+ return c->items.at(i);
+ }
+
+ return 0;
+}
+
+/*!
+ \overload
+
+ Returns a pointer to the first item whose text begins with \a
+ text, or 0 if no such item could be found. Use the \a compare flag
+ to control the comparison behavior.
+*/
+
+Q3IconViewItem *Q3IconView::findItem(const QString &text, ComparisonFlags compare) const
+{
+ if (!d->firstItem)
+ return 0;
+
+ if (compare == CaseSensitive || compare == 0)
+ compare |= ExactMatch;
+
+ QString itmtxt;
+ QString comtxt = text;
+ if (! (compare & CaseSensitive))
+ comtxt = text.toLower();
+
+ Q3IconViewItem *item;
+ if (d->currentItem)
+ item = d->currentItem;
+ else
+ item = d->firstItem;
+
+ Q3IconViewItem *beginsWithItem = 0;
+ Q3IconViewItem *endsWithItem = 0;
+ Q3IconViewItem *containsItem = 0;
+
+ if (item) {
+ for (; item; item = item->next) {
+ if (!(compare & CaseSensitive))
+ itmtxt = item->text().toLower();
+ else
+ itmtxt = item->text();
+
+ if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt)
+ return item;
+ if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt))
+ beginsWithItem = containsItem = item;
+ if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt))
+ endsWithItem = containsItem = item;
+ if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt))
+ containsItem = item;
+ }
+
+ if (d->currentItem && d->firstItem) {
+ item = d->firstItem;
+ for (; item && item != d->currentItem; item = item->next) {
+ if (!(compare & CaseSensitive))
+ itmtxt = item->text().toLower();
+ else
+ itmtxt = item->text();
+
+ if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt)
+ return item;
+ if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt))
+ beginsWithItem = containsItem = item;
+ if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt))
+ endsWithItem = containsItem = item;
+ if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt))
+ containsItem = item;
+ }
+ }
+ }
+
+ // Obey the priorities
+ if (beginsWithItem)
+ return beginsWithItem;
+ else if (endsWithItem)
+ return endsWithItem;
+ else if (containsItem)
+ return containsItem;
+ return 0;
+}
+
+/*!
+ Unselects all the items.
+*/
+
+void Q3IconView::clearSelection()
+{
+ selectAll(false);
+}
+
+/*!
+ In Multi and Extended modes, this function sets all items to be
+ selected if \a select is true, and to be unselected if \a select
+ is false.
+
+ In Single and NoSelection modes, this function only changes the
+ selection status of currentItem().
+*/
+
+void Q3IconView::selectAll(bool select)
+{
+ if (d->selectionMode == NoSelection)
+ return;
+
+ if (d->selectionMode == Single) {
+ if (d->currentItem)
+ d->currentItem->setSelected(select);
+ return;
+ }
+
+ bool b = signalsBlocked();
+ blockSignals(true);
+ Q3IconViewItem *item = d->firstItem;
+ Q3IconViewItem *i = d->currentItem;
+ bool changed = false;
+ bool ue = viewport()->updatesEnabled();
+ if (ue)
+ viewport()->setUpdatesEnabled(false);
+ QRect rr;
+ for (; item; item = item->next) {
+ if (select != item->isSelected()) {
+ item->setSelected(select, true);
+ rr = rr.united(item->rect());
+ changed = true;
+ }
+ }
+ if (ue)
+ viewport()->setUpdatesEnabled(true);
+ // we call updateContents not repaintContents because of possible previous updateContents
+ Q3ScrollView::updateContents(rr);
+ QApplication::sendPostedEvents(viewport(), QEvent::Paint);
+ if (i)
+ setCurrentItem(i);
+ blockSignals(b);
+ if (changed) {
+ emit selectionChanged();
+ }
+}
+
+/*!
+ Inverts the selection. Works only in Multi and Extended selection
+ mode.
+*/
+
+void Q3IconView::invertSelection()
+{
+ if (d->selectionMode == Single ||
+ d->selectionMode == NoSelection)
+ return;
+
+ bool b = signalsBlocked();
+ blockSignals(true);
+ Q3IconViewItem *item = d->firstItem;
+ for (; item; item = item->next)
+ item->setSelected(!item->isSelected(), true);
+ blockSignals(b);
+ emit selectionChanged();
+}
+
+/*!
+ Repaints the \a item.
+*/
+
+void Q3IconView::repaintItem(Q3IconViewItem *item)
+{
+ if (!item || item->dirty)
+ return;
+
+ if (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()).
+ intersects(QRect(item->x() - 1, item->y() - 1, item->width() + 2, item->height() + 2)))
+ repaintContents(item->x() - 1, item->y() - 1, item->width() + 2, item->height() + 2);
+}
+
+/*!
+ Repaints the selected items.
+*/
+void Q3IconView::repaintSelectedItems()
+{
+ if (selectionMode() == NoSelection)
+ return;
+
+ if (selectionMode() == Single) {
+ if (!currentItem() || !currentItem()->isSelected())
+ return;
+ QRect itemRect = currentItem()->rect(); //rect in contents coordinates
+ itemRect.moveBy(-contentsX(), -contentsY());
+ viewport()->update(itemRect);
+ } else {
+ // check if any selected items are visible
+ Q3IconViewItem *item = firstItem();
+ const QRect vr = QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+
+ while (item) {
+ if (item->isSelected() && item->rect().intersects(vr))
+ repaintItem(item);
+ item = item->nextItem();
+ }
+ }
+}
+
+/*!
+ Makes sure that \a item is entirely visible. If necessary,
+ ensureItemVisible() scrolls the icon view.
+
+ \sa ensureVisible()
+*/
+
+void Q3IconView::ensureItemVisible(Q3IconViewItem *item)
+{
+ if (!item)
+ return;
+
+ if ((d->updateTimer && d->updateTimer->isActive())
+ || (d->fullRedrawTimer && d->fullRedrawTimer->isActive()))
+ slotUpdate();
+
+ int w = item->width();
+ int h = item->height();
+ ensureVisible(item->x() + w / 2, item->y() + h / 2,
+ w / 2 + 1, h / 2 + 1);
+}
+
+/*!
+ Finds the first item whose bounding rectangle overlaps \a r and
+ returns a pointer to that item. \a r is given in content
+ coordinates. Returns 0 if no item overlaps \a r.
+
+ If you want to find all items that touch \a r, you will need to
+ use this function and nextItem() in a loop ending at
+ findLastVisibleItem() and test Q3IconViewItem::rect() for each of
+ these items.
+
+ \sa findLastVisibleItem() Q3IconViewItem::rect()
+*/
+
+Q3IconViewItem* Q3IconView::findFirstVisibleItem(const QRect &r) const
+{
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer;
+ Q3IconViewItem *i = 0;
+ bool alreadyIntersected = false;
+ for (; c; c = c->n) {
+ if (c->rect.intersects(r)) {
+ alreadyIntersected = true;
+ for (int j = 0; j < c->items.size(); ++j) {
+ Q3IconViewItem *item = c->items.at(j);
+ if (r.intersects(item->rect())) {
+ if (!i) {
+ i = item;
+ } else {
+ QRect r2 = item->rect();
+ QRect r3 = i->rect();
+ if (r2.y() < r3.y())
+ i = item;
+ else if (r2.y() == r3.y() &&
+ r2.x() < r3.x())
+ i = item;
+ }
+ }
+ }
+ } else {
+ if (alreadyIntersected)
+ break;
+ }
+ }
+
+ return i;
+}
+
+/*!
+ Finds the last item whose bounding rectangle overlaps \a r and
+ returns a pointer to that item. \a r is given in content
+ coordinates. Returns 0 if no item overlaps \a r.
+
+ \sa findFirstVisibleItem()
+*/
+
+Q3IconViewItem* Q3IconView::findLastVisibleItem(const QRect &r) const
+{
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer;
+ Q3IconViewItem *i = 0;
+ bool alreadyIntersected = false;
+ for (; c; c = c->n) {
+ if (c->rect.intersects(r)) {
+ alreadyIntersected = true;
+ for (int j = 0; j < c->items.size(); ++j) {
+ Q3IconViewItem *item = c->items.at(j);
+ if (r.intersects(item->rect())) {
+ if (!i) {
+ i = item;
+ } else {
+ QRect r2 = item->rect();
+ QRect r3 = i->rect();
+ if (r2.y() > r3.y())
+ i = item;
+ else if (r2.y() == r3.y() &&
+ r2.x() > r3.x())
+ i = item;
+ }
+ }
+ }
+ } else {
+ if (alreadyIntersected)
+ break;
+ }
+ }
+
+ return i;
+}
+
+/*!
+ Clears the icon view. All items are deleted.
+*/
+
+void Q3IconView::clear()
+{
+ setContentsPos(0, 0);
+ d->clearing = true;
+ bool block = signalsBlocked();
+ blockSignals(true);
+ clearSelection();
+ blockSignals(block);
+ setContentsPos(0, 0);
+ d->currentItem = 0;
+
+ if (!d->firstItem) {
+ d->clearing = false;
+ return;
+ }
+
+ Q3IconViewItem *item = d->firstItem, *tmp;
+ d->firstItem = 0;
+ while (item) {
+ tmp = item->next;
+ delete item;
+ item = tmp;
+ }
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc;
+ while (c) {
+ tmpc = c->n;
+ delete c;
+ c = tmpc;
+ }
+ d->firstContainer = d->lastContainer = 0;
+
+ d->count = 0;
+ d->lastItem = 0;
+ setCurrentItem(0);
+ d->highlightedItem = 0;
+ d->tmpCurrentItem = 0;
+ d->drawDragShapes = false;
+
+ resizeContents(0, 0);
+ // maybe we don't need this update, so delay it
+ d->fullRedrawTimer->start(0, true);
+
+ d->cleared = true;
+ d->clearing = false;
+}
+
+/*!
+ \property Q3IconView::gridX
+ \brief the horizontal grid of the icon view
+
+ If the value is -1, (the default), Q3IconView computes suitable
+ column widths based on the icon view's contents.
+
+ Note that setting a grid width overrides setMaxItemWidth().
+*/
+
+void Q3IconView::setGridX(int rx)
+{
+ d->rastX = rx >= 0 ? rx : -1;
+}
+
+/*!
+ \property Q3IconView::gridY
+ \brief the vertical grid of the icon view
+
+ If the value is -1, (the default), Q3IconView computes suitable
+ column heights based on the icon view's contents.
+*/
+
+void Q3IconView::setGridY(int ry)
+{
+ d->rastY = ry >= 0 ? ry : -1;
+}
+
+int Q3IconView::gridX() const
+{
+ return d->rastX;
+}
+
+int Q3IconView::gridY() const
+{
+ return d->rastY;
+}
+
+/*!
+ \property Q3IconView::spacing
+ \brief the space in pixels between icon view items
+
+ The default is 5 pixels.
+
+ Negative values for spacing are illegal.
+*/
+
+void Q3IconView::setSpacing(int sp)
+{
+ d->spacing = sp;
+}
+
+int Q3IconView::spacing() const
+{
+ return d->spacing;
+}
+
+/*!
+ \property Q3IconView::itemTextPos
+ \brief the position where the text of each item is drawn.
+
+ Valid values are \l Bottom or \l Right. The default is \l Bottom.
+*/
+
+void Q3IconView::setItemTextPos(ItemTextPos pos)
+{
+ if (pos == d->itemTextPos || (pos != Bottom && pos != Right))
+ return;
+
+ d->itemTextPos = pos;
+
+ Q3IconViewItem *item = d->firstItem;
+ for (; item; item = item->next) {
+ item->wordWrapDirty = true;
+ item->calcRect();
+ }
+
+ arrangeItemsInGrid(true);
+}
+
+Q3IconView::ItemTextPos Q3IconView::itemTextPos() const
+{
+ return d->itemTextPos;
+}
+
+/*!
+ \property Q3IconView::itemTextBackground
+ \brief the brush to use when drawing the background of an item's text.
+
+ By default this brush is set to Qt::NoBrush, meaning that only the
+ normal icon view background is used.
+*/
+
+void Q3IconView::setItemTextBackground(const QBrush &brush)
+{
+ d->itemTextBrush = brush;
+}
+
+QBrush Q3IconView::itemTextBackground() const
+{
+ return d->itemTextBrush;
+}
+
+/*!
+ \property Q3IconView::arrangement
+ \brief the arrangement mode of the icon view
+
+ This can be \l LeftToRight or \l TopToBottom. The default is \l
+ LeftToRight.
+*/
+
+void Q3IconView::setArrangement(Arrangement am)
+{
+ if (d->arrangement == am)
+ return;
+
+ d->arrangement = am;
+
+ viewport()->setUpdatesEnabled(false);
+ resizeContents(viewport()->width(), viewport()->height());
+ viewport()->setUpdatesEnabled(true);
+ arrangeItemsInGrid(true);
+}
+
+Q3IconView::Arrangement Q3IconView::arrangement() const
+{
+ return d->arrangement;
+}
+
+/*!
+ \property Q3IconView::resizeMode
+ \brief the resize mode of the icon view
+
+ This can be \l Fixed or \l Adjust. The default is \l Fixed.
+ See \l ResizeMode.
+*/
+
+void Q3IconView::setResizeMode(ResizeMode rm)
+{
+ if (d->resizeMode == rm)
+ return;
+
+ d->resizeMode = rm;
+}
+
+Q3IconView::ResizeMode Q3IconView::resizeMode() const
+{
+ return d->resizeMode;
+}
+
+/*!
+ \property Q3IconView::maxItemWidth
+ \brief the maximum width that an item may have.
+
+ The default is 100 pixels.
+
+ Note that if the gridX() value is set Q3IconView will ignore
+ this property.
+*/
+
+void Q3IconView::setMaxItemWidth(int w)
+{
+ d->maxItemWidth = w;
+}
+
+/*!
+ \property Q3IconView::maxItemTextLength
+ \brief the maximum length (in characters) that an item's text may have.
+
+ The default is 255 characters.
+*/
+
+void Q3IconView::setMaxItemTextLength(int w)
+{
+ d->maxItemTextLength = w;
+}
+
+int Q3IconView::maxItemWidth() const
+{
+ if (d->rastX != -1)
+ return d->rastX - 2;
+ else
+ return d->maxItemWidth;
+}
+
+int Q3IconView::maxItemTextLength() const
+{
+ return d->maxItemTextLength;
+}
+
+/*!
+ \property Q3IconView::itemsMovable
+ \brief whether the user is allowed to move items around in the icon view
+
+ The default is true.
+*/
+
+void Q3IconView::setItemsMovable(bool b)
+{
+ d->rearrangeEnabled = b;
+}
+
+bool Q3IconView::itemsMovable() const
+{
+ return d->rearrangeEnabled;
+}
+
+/*!
+ \property Q3IconView::autoArrange
+ \brief whether the icon view rearranges its items when a new item is inserted.
+
+ The default is true.
+
+ Note that if the icon view is not visible at the time of
+ insertion, Q3IconView defers all position-related work until it is
+ shown and then calls arrangeItemsInGrid().
+*/
+
+void Q3IconView::setAutoArrange(bool b)
+{
+ d->reorderItemsWhenInsert = b;
+}
+
+bool Q3IconView::autoArrange() const
+{
+ return d->reorderItemsWhenInsert;
+}
+
+/*!
+ If \a sort is true, this function sets the icon view to sort items
+ when a new item is inserted. If \a sort is false, the icon view
+ will not be sorted.
+
+ Note that autoArrange() must be true for sorting to take place.
+
+ If \a ascending is true (the default), items are sorted in
+ ascending order. If \a ascending is false, items are sorted in
+ descending order.
+
+ Q3IconViewItem::compare() is used to compare pairs of items. The
+ sorting is based on the items' keys; these default to the items'
+ text unless specifically set to something else.
+
+ \sa Q3IconView::setAutoArrange(), Q3IconView::autoArrange(),
+ sortDirection(), sort(), Q3IconViewItem::setKey()
+*/
+
+void Q3IconView::setSorting(bool sort, bool ascending)
+{
+ d->resortItemsWhenInsert = sort;
+ d->sortDirection = ascending;
+}
+
+/*!
+ \property Q3IconView::sorting
+ \brief whether the icon view sorts on insertion
+
+ The default is false, i.e. no sorting on insertion.
+
+ To set the sorting, use setSorting().
+*/
+
+bool Q3IconView::sorting() const
+{
+ return d->resortItemsWhenInsert;
+}
+
+/*!
+ \property Q3IconView::sortDirection
+ \brief whether the sort direction for inserting new items is ascending;
+
+ The default is true (i.e. ascending). This sort direction is only
+ meaningful if both sorting() and autoArrange() are true.
+
+ To set the sort direction, use setSorting()
+*/
+
+bool Q3IconView::sortDirection() const
+{
+ return d->sortDirection;
+}
+
+/*!
+ \property Q3IconView::wordWrapIconText
+ \brief whether the item text will be word-wrapped if it is too long
+
+ The default is true.
+
+ If this property is false, icon text that is too long is
+ truncated, and an ellipsis (...) appended to indicate that
+ truncation has occurred. The full text can still be seen by the
+ user if they hover the mouse because the full text is shown in a
+ tooltip; see setShowToolTips().
+*/
+
+void Q3IconView::setWordWrapIconText(bool b)
+{
+ if (d->wordWrapIconText == (uint)b)
+ return;
+
+ d->wordWrapIconText = b;
+ for (Q3IconViewItem *item = d->firstItem; item; item = item->next) {
+ item->wordWrapDirty = true;
+ item->calcRect();
+ }
+ arrangeItemsInGrid(true);
+}
+
+bool Q3IconView::wordWrapIconText() const
+{
+ return d->wordWrapIconText;
+}
+
+/*!
+ \property Q3IconView::showToolTips
+ \brief whether the icon view will display a tool tip with the complete text for any truncated item text
+
+ The default is true. Note that this has no effect if
+ setWordWrapIconText() is true, as it is by default.
+*/
+
+void Q3IconView::setShowToolTips(bool b)
+{
+ d->showTips = b;
+}
+
+bool Q3IconView::showToolTips() const
+{
+ return d->showTips;
+}
+
+/*!
+ \reimp
+*/
+void Q3IconView::contentsMousePressEvent(QMouseEvent *e)
+{
+ contentsMousePressEventEx(e);
+}
+
+void Q3IconView::contentsMousePressEventEx(QMouseEvent *e)
+{
+ if (d->rubber) {
+ d->dragging = false;
+ delete d->rubber;
+ d->rubber = 0;
+ viewport()->update();
+
+ if (d->scrollTimer) {
+ disconnect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(doAutoScroll()));
+ d->scrollTimer->stop();
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ }
+ }
+
+ d->dragStartPos = e->pos();
+ Q3IconViewItem *item = findItem(e->pos());
+ d->pressedItem = item;
+
+ if (item)
+ d->selectAnchor = item;
+
+#ifndef QT_NO_TEXTEDIT
+ if (d->renamingItem)
+ d->renamingItem->renameItem();
+#endif
+
+ if (!d->currentItem && !item && d->firstItem) {
+ d->currentItem = d->firstItem;
+ repaintItem(d->firstItem);
+ }
+
+ if (item && item->dragEnabled())
+ d->startDragItem = item;
+ else
+ d->startDragItem = 0;
+
+ if (e->button() == Qt::LeftButton && !(e->state() & Qt::ShiftButton) &&
+ !(e->state() & Qt::ControlButton) && item && item->isSelected() &&
+ item->textRect(false).contains(e->pos())) {
+
+ if (!item->renameEnabled()) {
+ d->mousePressed = true;
+#ifndef QT_NO_TEXTEDIT
+ } else {
+ ensureItemVisible(item);
+ setCurrentItem(item);
+ item->rename();
+ goto emit_signals;
+#endif
+ }
+ }
+
+ d->pressedSelected = item && item->isSelected();
+
+ if (item && item->isSelectable()) {
+ if (d->selectionMode == Single)
+ item->setSelected(true, e->state() & Qt::ControlButton);
+ else if (d->selectionMode == Multi && !item->isSelected())
+ item->setSelected(true, e->state() & Qt::ControlButton);
+ else if (d->selectionMode == Extended) {
+ if (e->state() & Qt::ShiftButton) {
+ d->pressedSelected = false;
+ bool block = signalsBlocked();
+ blockSignals(true);
+ viewport()->setUpdatesEnabled(false);
+ QRect r;
+ bool select = true;
+ if (d->currentItem)
+ r = QRect(qMin(d->currentItem->x(), item->x()),
+ qMin(d->currentItem->y(), item->y()),
+ 0, 0);
+ else
+ r = QRect(0, 0, 0, 0);
+ if (d->currentItem) {
+ if (d->currentItem->x() < item->x())
+ r.setWidth(item->x() - d->currentItem->x() + item->width());
+ else
+ r.setWidth(d->currentItem->x() - item->x() + d->currentItem->width());
+ if (d->currentItem->y() < item->y())
+ r.setHeight(item->y() - d->currentItem->y() + item->height());
+ else
+ r.setHeight(d->currentItem->y() - item->y() + d->currentItem->height());
+ r = r.normalized();
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer;
+ bool alreadyIntersected = false;
+ QRect redraw;
+ for (; c; c = c->n) {
+ if (c->rect.intersects(r)) {
+ alreadyIntersected = true;
+ for (int i = 0; i < c->items.size(); ++i) {
+ Q3IconViewItem *item = c->items.at(i);
+ if (r.intersects(item->rect())) {
+ redraw = redraw.united(item->rect());
+ item->setSelected(select, true);
+ }
+ }
+ } else {
+ if (alreadyIntersected)
+ break;
+ }
+ }
+ redraw = redraw.united(item->rect());
+ viewport()->setUpdatesEnabled(true);
+ repaintContents(redraw);
+ }
+ blockSignals(block);
+ viewport()->setUpdatesEnabled(true);
+ item->setSelected(select, true);
+ emit selectionChanged();
+ } else if (e->state() & Qt::ControlButton) {
+ d->pressedSelected = false;
+ item->setSelected(!item->isSelected(), e->state() & Qt::ControlButton);
+ } else {
+ item->setSelected(true, e->state() & Qt::ControlButton);
+ }
+ }
+ } else if ((d->selectionMode != Single || e->button() == Qt::RightButton)
+ && !(e->state() & Qt::ControlButton))
+ selectAll(false);
+
+ setCurrentItem(item);
+
+ if (e->button() == Qt::LeftButton) {
+ if (!item && (d->selectionMode == Multi ||
+ d->selectionMode == Extended)) {
+ d->tmpCurrentItem = d->currentItem;
+ d->currentItem = 0;
+ repaintItem(d->tmpCurrentItem);
+ if (d->rubber)
+ delete d->rubber;
+ d->rubber = 0;
+ d->rubber = new QRect(e->x(), e->y(), 0, 0);
+ d->selectedItems.clear();
+ if ((e->state() & Qt::ControlButton) == Qt::ControlButton) {
+ for (Q3IconViewItem *i = firstItem(); i; i = i->nextItem())
+ if (i->isSelected())
+ d->selectedItems.insert(i, i);
+ }
+ }
+
+ d->mousePressed = true;
+ }
+
+ emit_signals:
+ if (!d->rubber) {
+ emit mouseButtonPressed(e->button(), item, e->globalPos());
+ emit pressed(item);
+ emit pressed(item, e->globalPos());
+
+ if (e->button() == Qt::RightButton)
+ emit rightButtonPressed(item, e->globalPos());
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+ if (!receivers(SIGNAL(contextMenuRequested(Q3IconViewItem*,QPoint)))) {
+ e->ignore();
+ return;
+ }
+ if (e->reason() == QContextMenuEvent::Keyboard) {
+ Q3IconViewItem *item = currentItem();
+ QRect r = item ? item->rect() : QRect(0, 0, visibleWidth(), visibleHeight());
+ emit contextMenuRequested(item, viewport()->mapToGlobal(contentsToViewport(r.center())));
+ } else {
+ d->mousePressed = false;
+ Q3IconViewItem *item = findItem(e->pos());
+ emit contextMenuRequested(item, e->globalPos());
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ Q3IconViewItem *item = findItem(e->pos());
+ d->selectedItems.clear();
+
+ bool emitClicked = true;
+ d->mousePressed = false;
+ d->startDragItem = 0;
+
+ if (d->rubber) {
+ d->dragging = false;
+ viewport()->update();
+
+ if ((d->rubber->topLeft() - d->rubber->bottomRight()).manhattanLength() >
+ QApplication::startDragDistance())
+ emitClicked = false;
+ delete d->rubber;
+ d->rubber = 0;
+ d->currentItem = d->tmpCurrentItem;
+ d->tmpCurrentItem = 0;
+ if (d->currentItem)
+ repaintItem(d->currentItem);
+ }
+
+ if (d->scrollTimer) {
+ disconnect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(doAutoScroll()));
+ d->scrollTimer->stop();
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ }
+
+ if ((d->selectionMode == Extended || d->selectionMode == Multi) &&
+ d->currentItem == d->pressedItem &&
+ d->pressedSelected && d->currentItem) {
+ if (d->selectionMode == Extended) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ clearSelection();
+ blockSignals(block);
+ }
+ if (d->currentItem->isSelectable()) {
+ d->currentItem->selected = (d->selectionMode == Extended);
+ repaintItem(d->currentItem);
+ }
+ emit selectionChanged();
+ }
+ d->pressedItem = 0;
+
+ if (emitClicked) {
+ emit mouseButtonClicked(e->button(), item, e->globalPos());
+ emit clicked(item);
+ emit clicked(item, e->globalPos());
+ if (e->button() == Qt::RightButton)
+ emit rightButtonClicked(item, e->globalPos());
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ Q3IconViewItem *item = findItem(e->pos());
+ if (d->highlightedItem != item) {
+ if (item)
+ emit onItem(item);
+ else
+ emit onViewport();
+ d->highlightedItem = item;
+ }
+
+ if (d->mousePressed && e->state() == Qt::NoButton)
+ d->mousePressed = false;
+
+ if (d->startDragItem)
+ item = d->startDragItem;
+
+ if (d->mousePressed && d->startDragItem && item && item == d->currentItem &&
+ (item->isSelected() || d->selectionMode == NoSelection) && item->dragEnabled()) {
+ if ((d->dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) {
+ d->mousePressed = false;
+ d->cleared = false;
+#ifndef QT_NO_DRAGANDDROP
+ startDrag();
+#endif
+ if (d->tmpCurrentItem)
+ repaintItem(d->tmpCurrentItem);
+ }
+ } else if (d->mousePressed && !d->currentItem && d->rubber) {
+ doAutoScroll();
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::contentsMouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q3IconViewItem *item = findItem(e->pos());
+ if (item) {
+ selectAll(false);
+ item->setSelected(true, true);
+ emit doubleClicked(item);
+ }
+}
+
+/*!
+ \reimp
+*/
+
+#ifndef QT_NO_DRAGANDDROP
+void Q3IconView::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ d->dragging = true;
+ d->drawDragShapes = true;
+ d->tmpCurrentItem = 0;
+ initDragEnter(e);
+ d->oldDragPos = e->pos();
+ d->oldDragAcceptAction = false;
+ drawDragShapes(e->pos());
+ d->dropped = false;
+ e->accept();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if (e->pos() == d->oldDragPos) {
+ if (d->oldDragAcceptAction)
+ e->acceptAction();
+ else
+ e->ignore();
+ return;
+ }
+
+ drawDragShapes(d->oldDragPos);
+ d->dragging = false;
+
+ Q3IconViewItem *old = d->tmpCurrentItem;
+ d->tmpCurrentItem = 0;
+
+ Q3IconViewItem *item = findItem(e->pos());
+
+ if (item) {
+ if (old &&
+ old->rect().contains(d->oldDragPos) &&
+ !old->rect().contains(e->pos())) {
+ old->dragLeft();
+ repaintItem(old);
+ }
+ if (!item->rect().contains(d->oldDragPos))
+ item->dragEntered();
+ if (item->acceptDrop(e) || (item->isSelected() && e->source() == viewport())) {
+ d->oldDragAcceptAction = true;
+ e->acceptAction();
+ } else {
+ d->oldDragAcceptAction = false;
+ e->ignore();
+ }
+
+ d->tmpCurrentItem = item;
+ viewport()->update();
+ } else {
+ e->acceptAction();
+ d->oldDragAcceptAction = true;
+ if (old) {
+ old->dragLeft();
+ repaintItem(old);
+ }
+ }
+
+ d->oldDragPos = e->pos();
+ drawDragShapes(e->pos());
+ d->dragging = true;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::contentsDragLeaveEvent(QDragLeaveEvent *)
+{
+ if (!d->dropped)
+ drawDragShapes(d->oldDragPos);
+ d->dragging = false;
+
+ if (d->tmpCurrentItem) {
+ repaintItem(d->tmpCurrentItem);
+ d->tmpCurrentItem->dragLeft();
+ }
+
+ d->tmpCurrentItem = 0;
+ d->isIconDrag = false;
+ d->iconDragData.clear();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::contentsDropEvent(QDropEvent *e)
+{
+ d->dropped = true;
+ d->dragging = false;
+ drawDragShapes(d->oldDragPos);
+
+ if (d->tmpCurrentItem)
+ repaintItem(d->tmpCurrentItem);
+
+ Q3IconViewItem *i = findItem(e->pos());
+
+ if ((!i || i->isSelected()) && e->source() == viewport() && d->currentItem && !d->cleared) {
+ if (!d->rearrangeEnabled)
+ return;
+ QRect r = d->currentItem->rect();
+
+ d->currentItem->move(e->pos() - d->dragStart);
+
+ int w = d->currentItem->x() + d->currentItem->width() + 1;
+ int h = d->currentItem->y() + d->currentItem->height() + 1;
+
+ repaintItem(d->currentItem);
+ repaintContents(r.x(), r.y(), r.width(), r.height());
+
+ int dx = d->currentItem->x() - r.x();
+ int dy = d->currentItem->y() - r.y();
+
+ Q3IconViewItem *item = d->firstItem;
+ QRect rr;
+ for (; item; item = item->next) {
+ if (item->isSelected() && item != d->currentItem) {
+ rr = rr.united(item->rect());
+ item->moveBy(dx, dy);
+ rr = rr.united(item->rect());
+ }
+ w = qMax(w, item->x() + item->width() + 1);
+ h = qMax(h, item->y() + item->height() + 1);
+ }
+ repaintContents(rr);
+ bool fullRepaint = false;
+ if (w > contentsWidth() ||
+ h > contentsHeight())
+ fullRepaint = true;
+
+ int oldw = contentsWidth();
+ int oldh = contentsHeight();
+
+ resizeContents(w, h);
+
+
+ if (fullRepaint) {
+ repaintContents(oldw, 0, contentsWidth() - oldw, contentsHeight());
+ repaintContents(0, oldh, contentsWidth(), contentsHeight() - oldh);
+ }
+ e->acceptAction();
+ } else if (!i && (e->source() != viewport() || d->cleared)) {
+ QLinkedList<Q3IconDragItem> lst;
+ if (Q3IconDrag::canDecode(e)) {
+ QLinkedList<Q3IconDragDataItem> l;
+ Q3IconDragPrivate::decode(e, l);
+ QLinkedList<Q3IconDragDataItem>::Iterator it = l.begin();
+ for (; it != l.end(); ++it)
+ lst << (*it).data;
+ }
+ emit dropped(e, lst);
+ } else if (i) {
+ QLinkedList<Q3IconDragItem> lst;
+ if (Q3IconDrag::canDecode(e)) {
+ QLinkedList<Q3IconDragDataItem> l;
+ Q3IconDragPrivate::decode(e, l);
+ QLinkedList<Q3IconDragDataItem>::Iterator it = l.begin();
+ for (; it != l.end(); ++it)
+ lst << (*it).data;
+ }
+ i->dropped(e, lst);
+ }
+ d->isIconDrag = false;
+}
+#endif
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::resizeEvent(QResizeEvent* e)
+{
+ Q3ScrollView::resizeEvent(e);
+ if (d->resizeMode == Adjust) {
+ optimize_layout = true;
+ adjustItems();
+ optimize_layout = false;
+#if 0 // no need for timer delay anymore
+ d->oldSize = e->oldSize();
+ if (d->adjustTimer->isActive())
+ d->adjustTimer->stop();
+ d->adjustTimer->start(0, true);
+#endif
+ }
+}
+
+/*!
+ Adjusts the positions of the items to the geometry of the icon
+ view.
+*/
+
+void Q3IconView::adjustItems()
+{
+ d->adjustTimer->stop();
+ if (d->resizeMode == Adjust)
+ arrangeItemsInGrid(true);
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::keyPressEvent(QKeyEvent *e)
+{
+ if (!d->firstItem)
+ return;
+
+ if (!d->currentItem) {
+ setCurrentItem(d->firstItem);
+ if (d->selectionMode == Single)
+ d->currentItem->setSelected(true, true);
+ return;
+ }
+
+ bool selectCurrent = true;
+
+ switch (e->key()) {
+ case Qt::Key_Escape:
+ e->ignore();
+ break;
+#ifndef QT_NO_TEXTEDIT
+ case Qt::Key_F2: {
+ if (d->currentItem->renameEnabled()) {
+ d->currentItem->renameItem();
+ d->currentItem->rename();
+ return;
+ }
+ } break;
+#endif
+ case Qt::Key_Home: {
+ d->currInputString.clear();
+ if (!d->firstItem)
+ break;
+
+ selectCurrent = false;
+
+ Q3IconViewItem *item = 0;
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer;
+ while (!item && c) {
+ QList<Q3IconViewItem*> &list = c->items;
+ for (int j = 0; j < list.size(); ++j) {
+ Q3IconViewItem *i = list.at(j);
+ if (!item) {
+ item = i;
+ } else {
+ if (d->arrangement == LeftToRight) {
+ // we use pixmap so the items textlength are ignored
+ // find topmost, leftmost item
+ if (i->pixmapRect(false).y() < item->pixmapRect(false).y() ||
+ (i->pixmapRect(false).y() == item->pixmapRect(false).y() &&
+ i->pixmapRect(false).x() < item->pixmapRect(false).x()))
+ item = i;
+ } else {
+ // find leftmost, topmost item
+ if (i->pixmapRect(false).x() < item->pixmapRect(false).x() ||
+ (i->pixmapRect(false).x() == item->pixmapRect(false).x() &&
+ i->pixmapRect(false).y() < item->pixmapRect(false).y()))
+ item = i;
+ }
+ }
+ }
+ c = c->n;
+ }
+
+ if (item) {
+ Q3IconViewItem *old = d->currentItem;
+ setCurrentItem(item);
+ ensureItemVisible(item);
+ handleItemChange(old, e->state() & Qt::ShiftButton,
+ e->state() & Qt::ControlButton, true);
+ }
+ } break;
+ case Qt::Key_End: {
+ d->currInputString.clear();
+ if (!d->lastItem)
+ break;
+
+ selectCurrent = false;
+
+ Q3IconViewItem *item = 0;
+ Q3IconViewPrivate::ItemContainer *c = d->lastContainer;
+ while (!item && c) {
+ QList<Q3IconViewItem*> &list = c->items;
+ for (int j = 0; j < list.size(); ++j) {
+ Q3IconViewItem *i = list.at(j);
+ if (!item) {
+ item = i;
+ } else {
+ if (d->arrangement == LeftToRight) {
+ // find bottommost, rightmost item
+ if (i->pixmapRect(false).bottom() > item->pixmapRect(false).bottom() ||
+ (i->pixmapRect(false).bottom() == item->pixmapRect(false).bottom() &&
+ i->pixmapRect(false).right() > item->pixmapRect(false).right()))
+ item = i;
+ } else {
+ // find rightmost, bottommost item
+ if (i->pixmapRect(false).right() > item->pixmapRect(false).right() ||
+ (i->pixmapRect(false).right() == item->pixmapRect(false).right() &&
+ i->pixmapRect(false).bottom() > item->pixmapRect(false).bottom()))
+ item = i;
+ }
+ }
+ }
+ c = c->p;
+ }
+
+ if (item) {
+ Q3IconViewItem *old = d->currentItem;
+ setCurrentItem(item);
+ ensureItemVisible(item);
+ handleItemChange(old, e->state() & Qt::ShiftButton,
+ e->state() & Qt::ControlButton, true);
+ }
+ } break;
+ case Qt::Key_Right: {
+ d->currInputString.clear();
+ Q3IconViewItem *item;
+ selectCurrent = false;
+ Direction dir = DirRight;
+
+ QRect r(0, d->currentItem->y(), contentsWidth(), d->currentItem->height());
+ item = findItem(dir, d->currentItem->rect().center(), r);
+
+ // search the row below from the right
+ while (!item && r.y() < contentsHeight()) {
+ r.moveBy(0, d->currentItem->height());
+ item = findItem(dir, QPoint(0, r.center().y()), r);
+ }
+
+ if (item) {
+ Q3IconViewItem *old = d->currentItem;
+ setCurrentItem(item);
+ ensureItemVisible(item);
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ }
+ } break;
+ case Qt::Key_Left: {
+ d->currInputString.clear();
+ Q3IconViewItem *item;
+ selectCurrent = false;
+ Direction dir = DirLeft;
+
+ QRect r(0, d->currentItem->y(), contentsWidth(), d->currentItem->height());
+ item = findItem(dir, d->currentItem->rect().center(), r);
+
+ // search the row above from the left
+ while (!item && r.y() >= 0) {
+ r.moveBy(0, - d->currentItem->height());
+ item = findItem(dir, QPoint(contentsWidth(), r.center().y()), r);
+ }
+
+ if (item) {
+ Q3IconViewItem *old = d->currentItem;
+ setCurrentItem(item);
+ ensureItemVisible(item);
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ }
+ } break;
+ case Qt::Key_Space: {
+ d->currInputString.clear();
+ if (d->selectionMode == Single)
+ break;
+
+ d->currentItem->setSelected(!d->currentItem->isSelected(), true);
+ } break;
+ case Qt::Key_Enter: case Qt::Key_Return:
+ d->currInputString.clear();
+ emit returnPressed(d->currentItem);
+ break;
+ case Qt::Key_Down: {
+ d->currInputString.clear();
+ Q3IconViewItem *item;
+ selectCurrent = false;
+ Direction dir = DirDown;
+
+ QRect r(d->currentItem->x(), 0, d->currentItem->width(), contentsHeight());
+ item = findItem(dir, d->currentItem->rect().center(), r);
+
+ // finding the closest item below and to the right
+ while (!item && r.x() < contentsWidth()) {
+ r.moveBy(r.width() , 0);
+ item = findItem(dir, QPoint(r.center().x(), 0), r);
+ }
+
+
+ Q3IconViewItem *i = d->currentItem;
+ setCurrentItem(item);
+ item = i;
+ handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ } break;
+ case Qt::Key_Up: {
+ d->currInputString.clear();
+ Q3IconViewItem *item;
+ selectCurrent = false;
+ Direction dir = DirUp;
+
+ QRect r(d->currentItem->x(), 0, d->currentItem->width(), contentsHeight());
+ item = findItem(dir, d->currentItem->rect().center(), r);
+
+ // finding the closest item above and to the left
+ while (!item && r.x() >= 0) {
+ r.moveBy(- r.width(), 0);
+ item = findItem(dir, QPoint(r.center().x(), contentsHeight()), r);
+ }
+
+ Q3IconViewItem *i = d->currentItem;
+ setCurrentItem(item);
+ item = i;
+ handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ } break;
+ case Qt::Key_Next: {
+ d->currInputString.clear();
+ selectCurrent = false;
+ QRect r;
+ if (d->arrangement == LeftToRight)
+ r = QRect(0, d->currentItem->y() + visibleHeight(), contentsWidth(), visibleHeight());
+ else
+ r = QRect(d->currentItem->x() + visibleWidth(), 0, visibleWidth(), contentsHeight());
+ Q3IconViewItem *item = d->currentItem;
+ Q3IconViewItem *ni = findFirstVisibleItem(r );
+ if (!ni) {
+ if (d->arrangement == LeftToRight)
+ r = QRect(0, d->currentItem->y() + d->currentItem->height(), contentsWidth(), contentsHeight());
+ else
+ r = QRect(d->currentItem->x() + d->currentItem->width(), 0, contentsWidth(), contentsHeight());
+ ni = findLastVisibleItem(r );
+ }
+ if (ni) {
+ setCurrentItem(ni);
+ handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ }
+ } break;
+ case Qt::Key_Prior: {
+ d->currInputString.clear();
+ selectCurrent = false;
+ QRect r;
+ if (d->arrangement == LeftToRight)
+ r = QRect(0, d->currentItem->y() - visibleHeight(), contentsWidth(), visibleHeight());
+ else
+ r = QRect(d->currentItem->x() - visibleWidth(), 0, visibleWidth(), contentsHeight());
+ Q3IconViewItem *item = d->currentItem;
+ Q3IconViewItem *ni = findFirstVisibleItem(r );
+ if (!ni) {
+ if (d->arrangement == LeftToRight)
+ r = QRect(0, 0, contentsWidth(), d->currentItem->y());
+ else
+ r = QRect(0, 0, d->currentItem->x(), contentsHeight());
+ ni = findFirstVisibleItem(r );
+ }
+ if (ni) {
+ setCurrentItem(ni);
+ handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ }
+ } break;
+ default:
+ if (!e->text().isEmpty() && e->text()[0].isPrint()) {
+ selectCurrent = false;
+ Q3IconViewItem *i = d->currentItem;
+ if (!i)
+ i = d->firstItem;
+ if (!d->inputTimer->isActive()) {
+ d->currInputString = e->text();
+ i = i->next;
+ if (!i)
+ i = d->firstItem;
+ i = findItemByName(i);
+ } else {
+ d->inputTimer->stop();
+ d->currInputString += e->text();
+ i = findItemByName(i);
+ if (!i) {
+ d->currInputString = e->text();
+ if (d->currentItem && d->currentItem->next)
+ i = d->currentItem->next;
+ else
+ i = d->firstItem;
+ i = findItemByName(i);
+ }
+ }
+ if (i) {
+ setCurrentItem(i);
+ if (d->selectionMode == Extended) {
+ bool changed = false;
+ bool block = signalsBlocked();
+ blockSignals(true);
+ selectAll(false);
+ blockSignals(block);
+ if (!i->selected && i->isSelectable()) {
+ changed = true;
+ i->selected = true;
+ repaintItem(i);
+ }
+ if (changed)
+ emit selectionChanged();
+ }
+ }
+ d->inputTimer->start(400, true);
+ } else {
+ selectCurrent = false;
+ d->currInputString.clear();
+ if (e->state() & Qt::ControlButton) {
+ switch (e->key()) {
+ case Qt::Key_A:
+ selectAll(true);
+ break;
+ }
+ }
+ e->ignore();
+ return;
+ }
+ }
+
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->currentItem;
+
+ if (d->currentItem && !d->currentItem->isSelected() &&
+ d->selectionMode == Single && selectCurrent) {
+ d->currentItem->setSelected(true);
+ }
+
+ ensureItemVisible(d->currentItem);
+}
+
+/*
+ Finds the closest item in the Direction \a dir relative from the point \a relativeTo
+ which intersects with the searchRect.
+
+ The function chooses the closest item with its center in the \a searchRect.
+*/
+Q3IconViewItem* Q3IconView::findItem(Direction dir,
+ const QPoint &relativeTo,
+ const QRect &searchRect) const
+{
+ Q3IconViewItem *centerMatch = 0;
+ int centerMatchML = 0;
+
+ // gets list of containers with potential items
+ QList<Q3IconViewPrivate::ItemContainer * >* cList =
+ d->findContainers(dir, relativeTo, searchRect);
+
+ for (int i = 0; i < cList->size() && !centerMatch; ++i) {
+ QList<Q3IconViewItem *> &list = (cList->at(i))->items;
+ for (int j = 0; j < list.size(); ++j) {
+ Q3IconViewItem *item = list.at(j);
+ if (neighbourItem(dir, relativeTo, item) &&
+ searchRect.contains(item->rect().center()) && item != currentItem()) {
+ int ml = (relativeTo - item->rect().center()).manhattanLength();
+ if (centerMatch) {
+ if (ml < centerMatchML) {
+ centerMatch = item;
+ centerMatchML = ml;
+ }
+ } else {
+ centerMatch = item;
+ centerMatchML = ml;
+ }
+ }
+ }
+ }
+ return centerMatch;
+}
+
+
+/*
+ Returns true if the items orientation compared to
+ the point \a relativeTo is correct.
+*/
+bool Q3IconView::neighbourItem(Direction dir,
+ const QPoint &relativeTo,
+ const Q3IconViewItem *item) const
+{
+ switch (dir) {
+ case DirUp:
+ if (item->rect().center().y() < relativeTo.y())
+ return true;
+ break;
+ case DirDown:
+ if (item->rect().center().y() > relativeTo.y())
+ return true;
+ break;
+ case DirLeft:
+ if (item->rect().center().x() < relativeTo.x())
+ return true;
+ break;
+ case DirRight:
+ if (item->rect().center().x() > relativeTo.x())
+ return true;
+ break;
+ default:
+ // nothing
+ break;
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::focusInEvent(QFocusEvent *e)
+{
+ d->mousePressed = false;
+ d->inMenuMode = false;
+ if (d->currentItem) {
+ repaintItem(d->currentItem);
+ } else if (d->firstItem && e->reason() != Qt::MouseFocusReason) {
+ d->currentItem = d->firstItem;
+ emit currentChanged(d->currentItem);
+ repaintItem(d->currentItem);
+ }
+
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this))
+ repaintSelectedItems();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::focusOutEvent(QFocusEvent *e)
+{
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) {
+ d->inMenuMode =
+ e->reason() == Qt::PopupFocusReason ||
+ (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar"));
+ if (!d->inMenuMode)
+ repaintSelectedItems();
+ }
+ if (d->currentItem)
+ repaintItem(d->currentItem);
+}
+
+/*!
+ Draws the rubber band using the painter \a p.
+*/
+
+void Q3IconView::drawRubber(QPainter *p)
+{
+ if (!p || !d->rubber)
+ return;
+ QStyleOptionRubberBand opt;
+ opt.rect = d->rubber->normalized();
+ opt.shape = QRubberBand::Rectangle;
+ opt.palette = palette();
+ opt.state = QStyle::State_None;
+ style()->drawControl(QStyle::CE_RubberBand, &opt, p, this);
+}
+
+/*!
+ Returns the Q3DragObject that should be used for drag-and-drop.
+ This function is called by the icon view when starting a drag to
+ get the dragobject that should be used for the drag. Subclasses
+ may reimplement this.
+
+ \sa Q3IconDrag
+*/
+
+#ifndef QT_NO_DRAGANDDROP
+Q3DragObject *Q3IconView::dragObject()
+{
+ if (!d->currentItem)
+ return 0;
+
+ QPoint orig = d->dragStartPos;
+
+ Q3IconDrag *drag = new Q3IconDrag(viewport());
+ drag->setPixmap((d->currentItem->pixmap() ?
+ *d->currentItem->pixmap() : QPixmap()), // ### QPicture
+ QPoint(d->currentItem->pixmapRect().width() / 2,
+ d->currentItem->pixmapRect().height() / 2));
+
+ if (d->selectionMode == NoSelection) {
+ Q3IconViewItem *item = d->currentItem;
+ drag->append(Q3IconDragItem(),
+ QRect(item->pixmapRect(false).x() - orig.x(),
+ item->pixmapRect(false).y() - orig.y(),
+ item->pixmapRect().width(), item->pixmapRect().height()),
+ QRect(item->textRect(false).x() - orig.x(),
+ item->textRect(false).y() - orig.y(),
+ item->textRect().width(), item->textRect().height()));
+ } else {
+ for (Q3IconViewItem *item = d->firstItem; item; item = item->next) {
+ if (item->isSelected()) {
+ drag->append(Q3IconDragItem(),
+ QRect(item->pixmapRect(false).x() - orig.x(),
+ item->pixmapRect(false).y() - orig.y(),
+ item->pixmapRect().width(), item->pixmapRect().height()),
+ QRect(item->textRect(false).x() - orig.x(),
+ item->textRect(false).y() - orig.y(),
+ item->textRect().width(), item->textRect().height()));
+ }
+ }
+ }
+
+ return drag;
+}
+
+/*!
+ Starts a drag.
+*/
+
+void Q3IconView::startDrag()
+{
+ if (!d->startDragItem)
+ return;
+
+ QPoint orig = d->dragStartPos;
+ d->dragStart = QPoint(orig.x() - d->startDragItem->x(),
+ orig.y() - d->startDragItem->y());
+ d->startDragItem = 0;
+ d->mousePressed = false;
+ d->pressedItem = 0;
+ d->pressedSelected = 0;
+
+ Q3DragObject *drag = dragObject();
+ if (!drag)
+ return;
+
+ if (drag->drag())
+ if (drag->target() != viewport())
+ emit moved();
+}
+
+#endif
+
+/*!
+ Inserts the Q3IconViewItem \a item in the icon view's grid. \e{You
+ should never need to call this function.} Instead, insert
+ Q3IconViewItems by creating them with a pointer to the Q3IconView
+ that they are to be inserted into.
+*/
+
+void Q3IconView::insertInGrid(Q3IconViewItem *item)
+{
+ if (!item)
+ return;
+
+ if (d->reorderItemsWhenInsert) {
+ // #### make this efficient - but it's not too dramatic
+ int y = d->spacing;
+
+ item->dirty = false;
+ if (item == d->firstItem) {
+ bool dummy;
+ makeRowLayout(item, y, dummy);
+ return;
+ }
+
+ Q3IconViewItem *begin = rowBegin(item);
+ y = begin->y();
+ while (begin) {
+ bool dummy;
+ begin = makeRowLayout(begin, y, dummy);
+
+ if (!begin || !begin->next)
+ break;
+
+ begin = begin->next;
+ }
+ item->dirty = false;
+ } else {
+ QRegion r(QRect(0, 0, qMax(contentsWidth(), visibleWidth()),
+ qMax(contentsHeight(), visibleHeight())));
+
+ int y = -1;
+ for (Q3IconViewItem *i = d->firstItem; i; i = i->next) {
+ r = r.subtracted(i->rect());
+ y = qMax(y, i->y() + i->height());
+ }
+
+ QVector<QRect> rects = r.rects();
+ bool foundPlace = false;
+ for (int j = 0; j < rects.size(); ++j) {
+ const QRect rect = rects.at(j);
+ if (rect.width() >= item->width() &&
+ rect.height() >= item->height()) {
+ int sx = 0, sy = 0;
+ if (rect.width() >= item->width() + d->spacing)
+ sx = d->spacing;
+ if (rect.height() >= item->height() + d->spacing)
+ sy = d->spacing;
+ item->move(rect.x() + sx, rect.y() + sy);
+ foundPlace = true;
+ break;
+ }
+ }
+
+ if (!foundPlace)
+ item->move(d->spacing, y + d->spacing);
+
+ resizeContents(qMax(contentsWidth(), item->x() + item->width()),
+ qMax(contentsHeight(), item->y() + item->height()));
+ item->dirty = false;
+ }
+}
+
+/*!
+ Emits a signal to indicate selection changes. \a i is the
+ Q3IconViewItem that was selected or de-selected.
+
+ \e{You should never need to call this function.}
+*/
+
+void Q3IconView::emitSelectionChanged(Q3IconViewItem *i)
+{
+ emit selectionChanged();
+ if (d->selectionMode == Single)
+ emit selectionChanged(i ? i : d->currentItem);
+}
+
+/*!
+ \internal
+*/
+
+void Q3IconView::emitRenamed(Q3IconViewItem *item)
+{
+ if (!item)
+ return;
+
+ emit itemRenamed(item, item->text());
+ emit itemRenamed(item);
+}
+
+/*!
+ If a drag enters the icon view the shapes of the objects which the
+ drag contains are drawn, usnig \a pos as origin.
+*/
+
+void Q3IconView::drawDragShapes(const QPoint &pos)
+{
+#ifndef QT_NO_DRAGANDDROP
+ if (pos == QPoint(-1, -1))
+ return;
+
+ if (!d->drawDragShapes) {
+ d->drawDragShapes = true;
+ return;
+ }
+
+ d->dragPos = pos;
+ viewport()->update();
+#endif
+}
+
+/*!
+ When a drag enters the icon view, this function is called to
+ initialize it. Initializing in this context means getting
+ information about the drag, for example so that the icon view
+ knows enough about the drag to be able to draw drag shapes for the
+ drag data (e.g. shapes of icons which are dragged), etc.
+*/
+
+#ifndef QT_NO_DRAGANDDROP
+void Q3IconView::initDragEnter(QDropEvent *e)
+{
+ if (Q3IconDrag::canDecode(e)) {
+ Q3IconDragPrivate::decode(e, d->iconDragData);
+ d->isIconDrag = true;
+ } else if (Q3UriDrag::canDecode(e)) {
+ Q3StrList lst;
+ Q3UriDrag::decode(e, lst);
+ d->numDragItems = lst.count();
+ } else {
+ d->numDragItems = 0;
+ }
+
+}
+#endif
+
+/*!
+ This function is called to draw the rectangle \a r of the
+ background using the painter \a p.
+
+ The default implementation fills \a r with the viewport's
+ backgroundBrush(). Subclasses can reimplement this to draw custom
+ backgrounds.
+
+ \sa drawContents()
+*/
+
+void Q3IconView::drawBackground(QPainter *p, const QRect &r)
+{
+ p->fillRect(r, viewport()->backgroundBrush());
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3IconView::eventFilter(QObject * o, QEvent * e)
+{
+ if (o == viewport()) {
+ switch(e->type()) {
+ case QEvent::FocusIn:
+ focusInEvent((QFocusEvent*)e);
+ return true;
+ case QEvent::FocusOut:
+ focusOutEvent((QFocusEvent*)e);
+ return true;
+ case QEvent::Enter:
+ enterEvent(e);
+ return true;
+ case QEvent::Paint:
+ if (o == viewport()) {
+ viewportPaintEvent((QPaintEvent*)e);
+ QPainter p(viewport());
+ if (d->dragging) {
+ if (!d->rubber && d->drawDragShapes) {
+ p.setPen(QPen(Qt::color0));
+ QStyleOptionFocusRect opt;
+ opt.palette = palette();
+ opt.state = QStyle::State_KeyboardFocusChange;
+ opt.backgroundColor = palette().base().color();
+ if (d->isIconDrag) {
+ d->dragPos = contentsToViewport(d->dragPos);
+ QLinkedList<Q3IconDragDataItem>::Iterator it = d->iconDragData.begin();
+ for (; it != d->iconDragData.end(); ++it) {
+ QRect ir = (*it).item.pixmapRect();
+ QRect tr = (*it).item.textRect();
+ tr.moveBy(d->dragPos.x(), d->dragPos.y());
+ ir.moveBy(d->dragPos.x(), d->dragPos.y());
+ if (!ir.intersects(QRect(0, 0, visibleWidth(), visibleHeight())))
+ continue;
+ opt.rect = ir;
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this);
+ opt.rect = tr;
+ p.drawRect(tr);
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this);
+ }
+ } else if (d->numDragItems > 0) {
+ for (int i = 0; i < d->numDragItems; ++i) {
+ opt.rect.setRect(d->dragPos.x() + i * 40, d->dragPos.y(), 35, 35);
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this);
+ }
+
+ }
+ p.end();
+ }
+ } else {
+ p.translate(-contentsX(), -contentsY());
+ drawRubber(&p);
+ }
+ }
+ return true;
+#ifndef QT_NO_TOOLTIP
+ case QHelpEvent::ToolTip:
+ {
+ if (wordWrapIconText() || !showToolTips())
+ return false;
+
+ QHelpEvent *he = static_cast<QHelpEvent *>(e);
+ Q3IconViewItem *item = findItem(viewportToContents(he->pos()));
+ if (!item || item->tmpText == item->itemText) {
+ QToolTip::showText(he->globalPos(), QString(), viewport());
+ return true;
+ }
+
+ QToolTip::showText(he->globalPos(), item->itemText, viewport());
+ return true;
+ }
+#endif
+ default:
+ // nothing
+ break;
+ }
+ }
+
+ return Q3ScrollView::eventFilter(o, e);
+}
+
+
+/*!
+ \reimp
+*/
+
+QSize Q3IconView::minimumSizeHint() const
+{
+ return Q3ScrollView::minimumSizeHint();
+}
+
+/*!
+ \internal
+ Finds the next item after the start item beginning
+ with \a text.
+*/
+
+Q3IconViewItem* Q3IconView::findItemByName(Q3IconViewItem *start)
+{
+ if (!start)
+ return 0;
+ QString match = d->currInputString.toLower();
+ if (match.length() < 1)
+ return start;
+ QString curText;
+ Q3IconViewItem *i = start;
+ do {
+ curText = i->text().toLower();
+ if (curText.startsWith(match))
+ return i;
+ i = i->next;
+ if (!i)
+ i = d->firstItem;
+ } while (i != start);
+ return 0;
+}
+
+/*!
+ Lays out a row of icons (if Arrangement == \l TopToBottom this is
+ a column). Starts laying out with the item \a begin. \a y is the
+ starting coordinate. Returns the last item of the row (column) and
+ sets the new starting coordinate to \a y. The \a changed parameter
+ is used internally.
+
+ \warning This function may be made private in a future version of
+ Qt. We do not recommend calling it.
+*/
+
+Q3IconViewItem *Q3IconView::makeRowLayout(Q3IconViewItem *begin, int &y, bool &changed)
+{
+ Q3IconViewItem *end = 0;
+
+ bool reverse = QApplication::reverseLayout();
+ changed = false;
+
+ if (d->arrangement == LeftToRight) {
+
+ if (d->rastX == -1) {
+ // first calculate the row height
+ int h = 0;
+ int x = 0;
+ int ih = 0;
+ Q3IconViewItem *item = begin;
+ for (;;) {
+ x += d->spacing + item->width();
+ if (x > visibleWidth() && item != begin) {
+ item = item->prev;
+ break;
+ }
+ h = qMax(h, item->height());
+ ih = qMax(ih, item->pixmapRect().height());
+ Q3IconViewItem *old = item;
+ item = item->next;
+ if (!item) {
+ item = old;
+ break;
+ }
+ }
+ end = item;
+
+ if (d->rastY != -1)
+ h = qMax(h, d->rastY);
+
+ // now move the items
+ item = begin;
+ for (;;) {
+ item->dirty = false;
+ int x;
+ if (item == begin) {
+ if (reverse)
+ x = visibleWidth() - d->spacing - item->width();
+ else
+ x = d->spacing;
+ } else {
+ if (reverse)
+ x = item->prev->x() - item->width() - d->spacing;
+ else
+ x = item->prev->x() + item->prev->width() + d->spacing;
+ }
+ changed = item->move(x, y + ih - item->pixmapRect().height()) || changed;
+ if (y + h < item->y() + item->height())
+ h = qMax(h, ih + item->textRect().height());
+ if (item == end)
+ break;
+ item = item->next;
+ }
+ y += h + d->spacing;
+ } else {
+ // first calculate the row height
+ int h = begin->height();
+ int x = d->spacing;
+ int ih = begin->pixmapRect().height();
+ Q3IconViewItem *item = begin;
+ int i = 0;
+ int sp = 0;
+ for (;;) {
+ int r = calcGridNum(item->width(), d->rastX);
+ if (item == begin) {
+ i += r;
+ sp += r;
+ x = d->spacing + d->rastX * r;
+ } else {
+ sp += r;
+ i += r;
+ x = i * d->rastX + sp * d->spacing;
+ }
+ if (x > visibleWidth() && item != begin) {
+ item = item->prev;
+ break;
+ }
+ h = qMax(h, item->height());
+ ih = qMax(ih, item->pixmapRect().height());
+ Q3IconViewItem *old = item;
+ item = item->next;
+ if (!item) {
+ item = old;
+ break;
+ }
+ }
+ end = item;
+
+ if (d->rastY != -1)
+ h = qMax(h, d->rastY);
+
+ // now move the items
+ item = begin;
+ i = 0;
+ sp = 0;
+ for (;;) {
+ item->dirty = false;
+ int r = calcGridNum(item->width(), d->rastX);
+ if (item == begin) {
+ if (d->itemTextPos == Bottom)
+ changed = item->move(d->spacing + (r * d->rastX - item->width()) / 2,
+ y + ih - item->pixmapRect().height()) || changed;
+ else
+ changed = item->move(d->spacing, y + ih - item->pixmapRect().height()) || changed;
+ i += r;
+ sp += r;
+ } else {
+ sp += r;
+ int x = i * d->rastX + sp * d->spacing;
+ if (d->itemTextPos == Bottom)
+ changed = item->move(x + (r * d->rastX - item->width()) / 2,
+ y + ih - item->pixmapRect().height()) || changed;
+ else
+ changed = item->move(x, y + ih - item->pixmapRect().height()) || changed;
+ i += r;
+ }
+ if (y + h < item->y() + item->height())
+ h = qMax(h, ih + item->textRect().height());
+ if (item == end)
+ break;
+ item = item->next;
+ }
+ y += h + d->spacing;
+ }
+
+
+ } else { // -------------------------------- SOUTH ------------------------------
+
+ int x = y;
+
+ {
+ int w = 0;
+ int y = 0;
+ Q3IconViewItem *item = begin;
+ for (;;) {
+ y += d->spacing + item->height();
+ if (y > visibleHeight() && item != begin) {
+ item = item->prev;
+ break;
+ }
+ w = qMax(w, item->width());
+ Q3IconViewItem *old = item;
+ item = item->next;
+ if (!item) {
+ item = old;
+ break;
+ }
+ }
+ end = item;
+
+ if (d->rastX != -1)
+ w = qMax(w, d->rastX);
+
+ // now move the items
+ item = begin;
+ for (;;) {
+ item->dirty = false;
+ if (d->itemTextPos == Bottom) {
+ if (item == begin)
+ changed = item->move(x + (w - item->width()) / 2, d->spacing) || changed;
+ else
+ changed = item->move(x + (w - item->width()) / 2,
+ item->prev->y() + item->prev->height() + d->spacing) || changed;
+ } else {
+ if (item == begin)
+ changed = item->move(x, d->spacing) || changed;
+ else
+ changed = item->move(x, item->prev->y() + item->prev->height() + d->spacing) || changed;
+ }
+ if (item == end)
+ break;
+ item = item->next;
+ }
+ x += w + d->spacing;
+ }
+
+ y = x;
+ }
+
+ return end;
+}
+
+/*!
+ \internal
+ Calculates how many cells an item of width \a w needs in a grid with of
+ \a x and returns the result.
+*/
+
+int Q3IconView::calcGridNum(int w, int x) const
+{
+ float r = (float)w / (float)x;
+ if ((w / x) * x != w)
+ r += 1.0;
+ return (int)r;
+}
+
+/*!
+ \internal
+ Returns the first item of the row which contains \a item.
+*/
+
+Q3IconViewItem *Q3IconView::rowBegin(Q3IconViewItem *) const
+{
+ // #### todo
+ return d->firstItem;
+}
+
+/*!
+ Sorts and rearranges all the items in the icon view. If \a
+ ascending is true, the items are sorted in increasing order,
+ otherwise they are sorted in decreasing order.
+
+ Q3IconViewItem::compare() is used to compare pairs of items. The
+ sorting is based on the items' keys; these default to the items'
+ text unless specifically set to something else.
+
+ Note that this function sets the sort order to \a ascending.
+
+ \sa Q3IconViewItem::key(), Q3IconViewItem::setKey(),
+ Q3IconViewItem::compare(), Q3IconView::setSorting(),
+ Q3IconView::sortDirection()
+*/
+
+void Q3IconView::sort(bool ascending)
+{
+ if (count() == 0)
+ return;
+
+ d->sortDirection = ascending;
+ Q3IconViewPrivate::SortableItem *items = new Q3IconViewPrivate::SortableItem[count()];
+
+ Q3IconViewItem *item = d->firstItem;
+ int i = 0;
+ for (; item; item = item->next)
+ items[i++].item = item;
+
+ qsort(items, count(), sizeof(Q3IconViewPrivate::SortableItem), cmpIconViewItems);
+
+ Q3IconViewItem *prev = 0;
+ item = 0;
+ if (ascending) {
+ for (i = 0; i < (int)count(); ++i) {
+ item = items[i].item;
+ if (item) {
+ item->prev = prev;
+ if (item->prev)
+ item->prev->next = item;
+ item->next = 0;
+ }
+ if (i == 0)
+ d->firstItem = item;
+ if (i == (int)count() - 1)
+ d->lastItem = item;
+ prev = item;
+ }
+ } else {
+ for (i = (int)count() - 1; i >= 0 ; --i) {
+ item = items[i].item;
+ if (item) {
+ item->prev = prev;
+ if (item->prev)
+ item->prev->next = item;
+ item->next = 0;
+ }
+ if (i == (int)count() - 1)
+ d->firstItem = item;
+ if (i == 0)
+ d->lastItem = item;
+ prev = item;
+ }
+ }
+
+ delete [] items;
+
+ arrangeItemsInGrid(true);
+}
+
+/*!
+ \reimp
+*/
+
+QSize Q3IconView::sizeHint() const
+{
+ ensurePolished();
+
+ if (!d->firstItem)
+ return Q3ScrollView::sizeHint();
+
+ if (d->dirty && d->firstSizeHint) {
+ ((Q3IconView*)this)->resizeContents(qMax(400, contentsWidth()),
+ qMax(400, contentsHeight()));
+ if (autoArrange())
+ ((Q3IconView*)this)->arrangeItemsInGrid(false);
+ d->firstSizeHint = false;
+ }
+
+ d->dirty = true;
+ const QScrollBar *sb = verticalScrollBar();
+ QStyleOptionSlider opt;
+ opt.init(sb);
+ opt.orientation = sb->orientation();
+ int extra = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, sb) + 2 * frameWidth();
+ QSize s(qMin(400, contentsWidth() + extra),
+ qMin(400, contentsHeight() + extra));
+ return s;
+}
+
+/*!
+ \internal
+*/
+
+void Q3IconView::updateContents()
+{
+ viewport()->update();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3IconView::enterEvent(QEvent *e)
+{
+ Q3ScrollView::enterEvent(e);
+ emit onViewport();
+}
+
+/*!
+ \internal
+ This function is always called when the geometry of an item changes.
+ This function moves the item into the correct area in the internal
+ data structure.
+*/
+
+void Q3IconView::updateItemContainer(Q3IconViewItem *item)
+{
+ if (!item || d->containerUpdateLocked || (!isVisible() && autoArrange()))
+ return;
+
+ if (item->d->container1 && d->firstContainer) {
+ //Special-case to check if we can use removeLast otherwise use removeAll (slower)
+ if (item->d->container1->items.last() == item)
+ item->d->container1->items.removeLast();
+ else
+ item->d->container1->items.removeAll(item);
+ }
+ item->d->container1 = 0;
+ if (item->d->container2 && d->firstContainer) {
+ //Special-case to check if we can use removeLast otherwise use removeAll (slower)
+ if (item->d->container2->items.last() == item)
+ item->d->container2->items.removeLast();
+ else
+ item->d->container2->items.removeAll(item);
+ }
+ item->d->container2 = 0;
+
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer;
+ if (!c) {
+ appendItemContainer();
+ c = d->firstContainer;
+ }
+
+ const QRect irect = item->rect();
+ bool contains = false;
+ for (;;) {
+ if (c->rect.intersects(irect)) {
+ contains = c->rect.contains(irect);
+ break;
+ }
+
+ c = c->n;
+ if (!c) {
+ appendItemContainer();
+ c = d->lastContainer;
+ }
+ }
+
+ if (!c) {
+ qWarning("Q3IconViewItem::updateItemContainer(): No fitting container found!");
+ return;
+ }
+
+ c->items.append(item);
+ item->d->container1 = c;
+
+ if (!contains) {
+ c = c->n;
+ if (!c) {
+ appendItemContainer();
+ c = d->lastContainer;
+ }
+ c->items.append(item);
+ item->d->container2 = c;
+ }
+ if (contentsWidth() < irect.right() || contentsHeight() < irect.bottom())
+ resizeContents(qMax(contentsWidth(), irect.right()), qMax(contentsHeight(), irect.bottom()));
+}
+
+/*!
+ \internal
+ Appends a new rect area to the internal data structure of the items.
+*/
+
+void Q3IconView::appendItemContainer()
+{
+ QSize s;
+ // #### We have to find out which value is best here
+ if (d->arrangement == LeftToRight)
+ s = QSize(INT_MAX - 1, RECT_EXTENSION);
+ else
+ s = QSize(RECT_EXTENSION, INT_MAX - 1);
+
+ if (!d->firstContainer) {
+ d->firstContainer = new Q3IconViewPrivate::ItemContainer(0, 0, QRect(QPoint(0, 0), s));
+ d->lastContainer = d->firstContainer;
+ } else {
+ if (d->arrangement == LeftToRight)
+ d->lastContainer = new Q3IconViewPrivate::ItemContainer(
+ d->lastContainer, 0, QRect(d->lastContainer->rect.bottomLeft(), s));
+ else
+ d->lastContainer = new Q3IconViewPrivate::ItemContainer(
+ d->lastContainer, 0, QRect(d->lastContainer->rect.topRight(), s));
+ }
+}
+
+/*! \internal
+
+ Rebuilds the whole internal data structure. This is done when it's
+ likely that most/all items change their geometry (e.g. in
+ arrangeItemsInGrid()), because calling this is then more efficient
+ than calling updateItemContainer() for each item.
+*/
+
+void Q3IconView::rebuildContainers()
+{
+ Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc;
+ while (c) {
+ tmpc = c->n;
+ delete c;
+ c = tmpc;
+ }
+ d->firstContainer = d->lastContainer = 0;
+
+ Q3IconViewItem *item = d->firstItem;
+ appendItemContainer();
+ c = d->lastContainer;
+ while (item) {
+ if (c->rect.contains(item->rect())) {
+ item->d->container1 = c;
+ item->d->container2 = 0;
+ c->items.append(item);
+ item = item->next;
+ } else if (c->rect.intersects(item->rect())) {
+ item->d->container1 = c;
+ c->items.append(item);
+ c = c->n;
+ if (!c) {
+ appendItemContainer();
+ c = d->lastContainer;
+ }
+ c->items.append(item);
+ item->d->container2 = c;
+ item = item->next;
+ c = c->p;
+ } else {
+ if (d->arrangement == LeftToRight) {
+ if (item->y() < c->rect.y() && c->p) {
+ c = c->p;
+ continue;
+ }
+ } else {
+ if (item->x() < c->rect.x() && c->p) {
+ c = c->p;
+ continue;
+ }
+ }
+
+ c = c->n;
+ if (!c) {
+ appendItemContainer();
+ c = d->lastContainer;
+ }
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+
+void Q3IconView::movedContents(int, int)
+{
+ if (d->drawDragShapes) {
+ drawDragShapes(d->oldDragPos);
+ d->oldDragPos = QPoint(-1, -1);
+ }
+}
+
+void Q3IconView::handleItemChange(Q3IconViewItem *old, bool shift,
+ bool control, bool homeend)
+{
+ if (d->selectionMode == Single) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ if (old)
+ old->setSelected(false);
+ blockSignals(block);
+ d->currentItem->setSelected(true, true);
+ } else if (d->selectionMode == Extended) {
+ if (shift) {
+ if (!d->selectAnchor) {
+ if (old && !old->selected && old->isSelectable()) {
+ old->selected = true;
+ repaintItem(old);
+ }
+ d->currentItem->setSelected(true, true);
+ } else {
+ Q3IconViewItem *from = d->selectAnchor, *to = d->currentItem;
+ if (!from || !to)
+ return;
+
+ // checking if it's downwards and if we span rows
+ bool downwards = false;
+ bool spanning = false;
+ if (d->arrangement == LeftToRight) {
+ if (from->rect().center().y() < to->rect().center().y())
+ downwards = true;
+ } else {
+ if (from->rect().center().x() < to->rect().center().x())
+ downwards = true;
+ }
+
+ QRect fr = from->rect();
+ QRect tr = to->rect();
+ if (d->arrangement == LeftToRight) {
+ fr.moveTopLeft(QPoint(tr.x(), fr.y()));
+ if (!tr.intersects(fr))
+ spanning = true;
+ } else {
+ fr.moveTopLeft(QPoint(fr.x(), tr.y()));
+ if (!tr.intersects(fr))
+ spanning = true;
+ }
+
+
+ // finding the rectangles
+ QRect topRect, bottomRect, midRect;
+ if (!spanning) {
+ midRect = from->rect().united(to->rect());
+ } else {
+ if (downwards) {
+ topRect = from->rect();
+ bottomRect = to->rect();
+ } else {
+ topRect = to->rect();
+ bottomRect = from->rect();
+ }
+ if (d->arrangement == LeftToRight) {
+ topRect.setRight(contentsWidth());
+ bottomRect.setLeft(0);
+ midRect.setRect(0, topRect.bottom(),
+ contentsWidth(),
+ bottomRect.top() - topRect.bottom());
+ } else {
+ topRect.setBottom(contentsHeight());
+ bottomRect.setTop(0);
+ midRect.setRect(topRect.right(),
+ 0,
+ bottomRect.left() - topRect.right(),
+ contentsHeight());
+ }
+ }
+
+ // finding contained items and selecting them
+ Q3IconViewItem *item = 0;
+ bool changed = false;
+ bool midValid = midRect.isValid();
+ bool topValid = topRect.isValid();
+ bool bottomValid = bottomRect.isValid();
+ QRect selectedRect, unselectedRect;
+ for (item = d->firstItem; item; item = item->next) {
+ bool contained = false;
+ QPoint itemCenter = item->rect().center();
+ if (midValid && midRect.contains(itemCenter))
+ contained = true;
+ if (!contained && topValid && topRect.contains(itemCenter))
+ contained = true;
+ if (!contained && bottomValid && bottomRect.contains(itemCenter))
+ contained = true;
+
+ if (contained) {
+ if (!item->selected && item->isSelectable()) {
+ changed = true;
+ item->selected = true;
+ selectedRect = selectedRect.united(item->rect());
+ }
+ } else if (item->selected && !control) {
+ item->selected = false;
+ unselectedRect = unselectedRect.united(item->rect());
+ changed = true;
+ }
+ }
+
+ QRect viewRect(contentsX(), contentsY(),
+ visibleWidth(), visibleHeight());
+
+ if (viewRect.intersects(selectedRect)) {
+ if (homeend)
+ Q3ScrollView::updateContents(viewRect.intersected(selectedRect));
+ else
+ repaintContents(viewRect.intersected(selectedRect));
+ }
+ if (viewRect.intersects(unselectedRect)) {
+ if (homeend)
+ Q3ScrollView::updateContents(viewRect.intersected(unselectedRect));
+ else
+ repaintContents(viewRect.intersected(unselectedRect));
+ }
+
+ if (changed)
+ emit selectionChanged();
+ }
+ } else if (!control) {
+ blockSignals(true);
+ selectAll(false);
+ blockSignals(false);
+ d->currentItem->setSelected(true, true);
+ }
+ } else {
+ if (shift)
+ d->currentItem->setSelected(!d->currentItem->isSelected(), true);
+ }
+}
+
+QBitmap Q3IconView::mask(QPixmap *pix) const
+{
+ QBitmap m;
+ if (d->maskCache.find(QString::number(pix->serialNumber()), m))
+ return m;
+ if (pix->hasAlphaChannel())
+ m = pix->mask();
+ else
+ m = pix->createHeuristicMask();
+ d->maskCache.insert(QString::number(pix->serialNumber()), m);
+ return m;
+}
+
+/*!
+ Returns true if an iconview item is being renamed; otherwise
+ returns false.
+*/
+
+bool Q3IconView::isRenaming() const
+{
+#ifndef QT_NO_TEXTEDIT
+ return d->renamingItem && d->renamingItem->renameBox;
+#else
+ return false;
+#endif
+}
+
+/*!
+ \enum Q3IconView::StringComparisonMode
+
+ This enum type is used to set the string comparison mode when
+ searching for an item. We'll refer to the string being searched
+ as the 'target' string.
+
+ \value CaseSensitive The strings must match case sensitively.
+ \value ExactMatch The target and search strings must match exactly.
+ \value BeginsWith The target string begins with the search string.
+ \value EndsWith The target string ends with the search string.
+ \value Contains The target string contains the search string.
+
+ If you OR these flags together (excluding \c CaseSensitive), the
+ search criteria be applied in the following order: \c ExactMatch,
+ \c BeginsWith, \c EndsWith, \c Contains.
+
+ Matching is case-insensitive unless \c CaseSensitive is set. \c
+ CaseSensitive can be OR-ed with any combination of the other
+ flags.
+
+ \sa ComparisonFlags
+*/
+
+/*!
+ \typedef Q3IconView::ComparisonFlags
+
+ This typedef is used in Q3IconView's API for values that are OR'd
+ combinations of \l StringComparisonMode values.
+
+ \sa StringComparisonMode
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ICONVIEW
diff --git a/src/qt3support/itemviews/q3iconview.h b/src/qt3support/itemviews/q3iconview.h
new file mode 100644
index 0000000..77f475b
--- /dev/null
+++ b/src/qt3support/itemviews/q3iconview.h
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3ICONVIEW_H
+#define Q3ICONVIEW_H
+
+#include <Qt3Support/q3scrollview.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qsize.h>
+#include <QtGui/qfont.h> // QString->QFont conversion
+#include <Qt3Support/q3dragobject.h>
+#include <QtGui/qbitmap.h>
+#include <QtGui/qpicture.h>
+#include <Qt3Support/q3valuelist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_ICONVIEW
+
+class Q3IconView;
+class QPainter;
+class QMimeSource;
+class QMouseEvent;
+class QDragEnterEvent;
+class QDragMoveEvent;
+class QDragLeaveEvent;
+class QKeyEvent;
+class QFocusEvent;
+class QShowEvent;
+class Q3IconViewItem;
+class Q3IconViewItemLineEdit;
+class QStringList;
+class Q3IconDragPrivate;
+class QColorGroup;
+
+#ifndef QT_NO_DRAGANDDROP
+
+class Q_COMPAT_EXPORT Q3IconDragItem
+{
+public:
+ Q3IconDragItem();
+ virtual ~Q3IconDragItem();
+ virtual QByteArray data() const;
+ virtual void setData(const QByteArray &d);
+ bool operator== (const Q3IconDragItem&) const;
+
+private:
+ QByteArray ba;
+
+};
+
+class Q_COMPAT_EXPORT Q3IconDrag : public Q3DragObject
+{
+ Q_OBJECT
+public:
+ Q3IconDrag(QWidget * dragSource, const char* name = 0);
+ virtual ~Q3IconDrag();
+
+ void append(const Q3IconDragItem &item, const QRect &pr, const QRect &tr);
+
+ virtual const char* format(int i) const;
+ static bool canDecode(QMimeSource* e);
+ virtual QByteArray encodedData(const char* mime) const;
+
+private:
+ Q_DISABLE_COPY(Q3IconDrag)
+
+ Q3IconDragPrivate *d;
+ QChar endMark;
+
+ friend class Q3IconView;
+ friend class Q3IconViewPrivate;
+};
+
+#endif
+
+class Q3IconViewToolTip;
+class Q3IconViewItemPrivate;
+
+class Q_COMPAT_EXPORT Q3IconViewItem
+{
+ friend class Q3IconView;
+ friend class Q3IconViewToolTip;
+ friend class Q3IconViewItemLineEdit;
+
+public:
+ Q3IconViewItem(Q3IconView *parent);
+ Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after);
+ Q3IconViewItem(Q3IconView *parent, const QString &text);
+ Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, const QString &text);
+ Q3IconViewItem(Q3IconView *parent, const QString &text, const QPixmap &icon);
+ Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, const QString &text, const QPixmap &icon);
+#ifndef QT_NO_PICTURE
+ Q3IconViewItem(Q3IconView *parent, const QString &text, const QPicture &picture);
+ Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, const QString &text, const QPicture &picture);
+#endif
+ virtual ~Q3IconViewItem();
+
+ virtual void setRenameEnabled(bool allow);
+ virtual void setDragEnabled(bool allow);
+ virtual void setDropEnabled(bool allow);
+
+ virtual QString text() const;
+ virtual QPixmap *pixmap() const;
+#ifndef QT_NO_PICTURE
+ virtual QPicture *picture() const;
+#endif
+ virtual QString key() const;
+
+ bool renameEnabled() const;
+ bool dragEnabled() const;
+ bool dropEnabled() const;
+
+ Q3IconView *iconView() const;
+ Q3IconViewItem *prevItem() const;
+ Q3IconViewItem *nextItem() const;
+
+ int index() const;
+
+ virtual void setSelected(bool s, bool cb);
+ virtual void setSelected(bool s);
+ virtual void setSelectable(bool s);
+
+ bool isSelected() const;
+ bool isSelectable() const;
+
+ virtual void repaint();
+
+ virtual bool move(int x, int y);
+ virtual void moveBy(int dx, int dy);
+ virtual bool move(const QPoint &pnt);
+ virtual void moveBy(const QPoint &pnt);
+
+ QRect rect() const;
+ int x() const;
+ int y() const;
+ int width() const;
+ int height() const;
+ QSize size() const;
+ QPoint pos() const;
+ QRect textRect(bool relative = true) const;
+ QRect pixmapRect(bool relative = true) const;
+ bool contains(const QPoint& pnt) const;
+ bool intersects(const QRect& r) const;
+
+ virtual bool acceptDrop(const QMimeSource *mime) const;
+
+#ifndef QT_NO_TEXTEDIT
+ void rename();
+#endif
+
+ virtual int compare(Q3IconViewItem *i) const;
+
+ virtual void setText(const QString &text);
+ virtual void setPixmap(const QPixmap &icon);
+#ifndef QT_NO_PICTURE
+ virtual void setPicture(const QPicture &icon);
+#endif
+ virtual void setText(const QString &text, bool recalc, bool redraw = true);
+ virtual void setPixmap(const QPixmap &icon, bool recalc, bool redraw = true);
+ virtual void setKey(const QString &k);
+
+ virtual int rtti() const;
+ static int RTTI;
+
+protected:
+#ifndef QT_NO_TEXTEDIT
+ virtual void removeRenameBox();
+#endif
+ virtual void calcRect(const QString &text_ = QString());
+ virtual void paintItem(QPainter *p, const QColorGroup &cg);
+ virtual void paintFocus(QPainter *p, const QColorGroup &cg);
+#ifndef QT_NO_DRAGANDDROP
+ virtual void dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst);
+#endif
+ virtual void dragEntered();
+ virtual void dragLeft();
+ void setItemRect(const QRect &r);
+ void setTextRect(const QRect &r);
+ void setPixmapRect(const QRect &r);
+ void calcTmpText();
+ QString tempText() const;
+
+private:
+ void init(Q3IconViewItem *after = 0
+#ifndef QT_NO_PICTURE
+ , QPicture *pic = 0
+#endif
+ );
+#ifndef QT_NO_TEXTEDIT
+ void renameItem();
+ void cancelRenameItem();
+#endif
+ void checkRect();
+
+ Q3IconView *view;
+ QString itemText, itemKey;
+ QString tmpText;
+ QPixmap *itemIcon;
+#ifndef QT_NO_PICTURE
+ QPicture *itemPic;
+#endif
+ Q3IconViewItem *prev, *next;
+ uint allow_rename : 1;
+ uint allow_drag : 1;
+ uint allow_drop : 1;
+ uint selected : 1;
+ uint selectable : 1;
+ uint dirty : 1;
+ uint wordWrapDirty : 1;
+ QRect itemRect, itemTextRect, itemIconRect;
+#ifndef QT_NO_TEXTEDIT
+ Q3IconViewItemLineEdit *renameBox;
+#endif
+ QRect oldRect;
+
+ Q3IconViewItemPrivate *d;
+
+};
+
+class Q3IconViewPrivate;
+
+class Q_COMPAT_EXPORT Q3IconView : public Q3ScrollView
+{
+ friend class Q3IconViewItem;
+ friend class Q3IconViewPrivate;
+ friend class Q3IconViewToolTip;
+
+ Q_OBJECT
+ // #### sorting and sort direction do not work
+ Q_ENUMS(SelectionMode ItemTextPos Arrangement ResizeMode)
+ Q_PROPERTY(bool sorting READ sorting)
+ Q_PROPERTY(bool sortDirection READ sortDirection)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode)
+ Q_PROPERTY(int gridX READ gridX WRITE setGridX)
+ Q_PROPERTY(int gridY READ gridY WRITE setGridY)
+ Q_PROPERTY(int spacing READ spacing WRITE setSpacing)
+ Q_PROPERTY(ItemTextPos itemTextPos READ itemTextPos WRITE setItemTextPos)
+ Q_PROPERTY(QBrush itemTextBackground READ itemTextBackground WRITE setItemTextBackground)
+ Q_PROPERTY(Arrangement arrangement READ arrangement WRITE setArrangement)
+ Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode)
+ Q_PROPERTY(int maxItemWidth READ maxItemWidth WRITE setMaxItemWidth)
+ Q_PROPERTY(int maxItemTextLength READ maxItemTextLength WRITE setMaxItemTextLength)
+ Q_PROPERTY(bool autoArrange READ autoArrange WRITE setAutoArrange)
+ Q_PROPERTY(bool itemsMovable READ itemsMovable WRITE setItemsMovable)
+ Q_PROPERTY(bool wordWrapIconText READ wordWrapIconText WRITE setWordWrapIconText)
+ Q_PROPERTY(bool showToolTips READ showToolTips WRITE setShowToolTips)
+ Q_PROPERTY(uint count READ count)
+
+public:
+ enum SelectionMode {
+ Single = 0,
+ Multi,
+ Extended,
+ NoSelection
+ };
+ enum Arrangement {
+ LeftToRight = 0,
+ TopToBottom
+ };
+ enum ResizeMode {
+ Fixed = 0,
+ Adjust
+ };
+ enum ItemTextPos {
+ Bottom = 0,
+ Right
+ };
+
+ Q3IconView(QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0);
+ virtual ~Q3IconView();
+
+ virtual void insertItem(Q3IconViewItem *item, Q3IconViewItem *after = 0L);
+ virtual void takeItem(Q3IconViewItem *item);
+
+ int index(const Q3IconViewItem *item) const;
+
+ Q3IconViewItem *firstItem() const;
+ Q3IconViewItem *lastItem() const;
+ Q3IconViewItem *currentItem() const;
+ virtual void setCurrentItem(Q3IconViewItem *item);
+ virtual void setSelected(Q3IconViewItem *item, bool s, bool cb = false);
+
+ uint count() const;
+
+public:
+ virtual void showEvent(QShowEvent *);
+
+ virtual void setSelectionMode(SelectionMode m);
+ SelectionMode selectionMode() const;
+
+ Q3IconViewItem *findItem(const QPoint &pos) const;
+
+ enum StringComparisonMode {
+ CaseSensitive = 0x00001, // 0 0001
+ BeginsWith = 0x00002, // 0 0010
+ EndsWith = 0x00004, // 0 0100
+ Contains = 0x00008, // 0 1000
+ ExactMatch = 0x00010 // 1 0000
+ };
+ typedef uint ComparisonFlags;
+ Q3IconViewItem *findItem(const QString &text, ComparisonFlags = BeginsWith | Qt::CaseSensitive) const;
+ virtual void selectAll(bool select);
+ virtual void clearSelection();
+ virtual void invertSelection();
+
+ virtual void repaintItem(Q3IconViewItem *item);
+ void repaintSelectedItems();
+
+ void ensureItemVisible(Q3IconViewItem *item);
+ Q3IconViewItem* findFirstVisibleItem(const QRect &r) const;
+ Q3IconViewItem* findLastVisibleItem(const QRect &r) const;
+
+ virtual void clear();
+
+ virtual void setGridX(int rx);
+ virtual void setGridY(int ry);
+ int gridX() const;
+ int gridY() const;
+ virtual void setSpacing(int sp);
+ int spacing() const;
+ virtual void setItemTextPos(ItemTextPos pos);
+ ItemTextPos itemTextPos() const;
+ virtual void setItemTextBackground(const QBrush &b);
+ QBrush itemTextBackground() const;
+ virtual void setArrangement(Arrangement am);
+ Arrangement arrangement() const;
+ virtual void setResizeMode(ResizeMode am);
+ ResizeMode resizeMode() const;
+ virtual void setMaxItemWidth(int w);
+ int maxItemWidth() const;
+ virtual void setMaxItemTextLength(int w);
+ int maxItemTextLength() const;
+ virtual void setAutoArrange(bool b);
+ bool autoArrange() const;
+ virtual void setShowToolTips(bool b);
+ bool showToolTips() const;
+
+ void setSorting(bool sort, bool ascending = true);
+ bool sorting() const;
+ bool sortDirection() const;
+
+ virtual void setItemsMovable(bool b);
+ bool itemsMovable() const;
+ virtual void setWordWrapIconText(bool b);
+ bool wordWrapIconText() const;
+
+ bool eventFilter(QObject * o, QEvent *);
+
+ QSize minimumSizeHint() const;
+ QSize sizeHint() const;
+
+ virtual void sort(bool ascending = true);
+
+ bool isRenaming() const;
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+public Q_SLOTS:
+ virtual void arrangeItemsInGrid(const QSize &grid, bool update = true);
+ virtual void arrangeItemsInGrid(bool update = true);
+ virtual void setContentsPos(int x, int y);
+ virtual void updateContents();
+
+Q_SIGNALS:
+ void selectionChanged();
+ void selectionChanged(Q3IconViewItem *item);
+ void currentChanged(Q3IconViewItem *item);
+ void clicked(Q3IconViewItem *);
+ void clicked(Q3IconViewItem *, const QPoint &);
+ void pressed(Q3IconViewItem *);
+ void pressed(Q3IconViewItem *, const QPoint &);
+
+ void doubleClicked(Q3IconViewItem *item);
+ void returnPressed(Q3IconViewItem *item);
+ void rightButtonClicked(Q3IconViewItem* item, const QPoint& pos);
+ void rightButtonPressed(Q3IconViewItem* item, const QPoint& pos);
+ void mouseButtonPressed(int button, Q3IconViewItem* item, const QPoint& pos);
+ void mouseButtonClicked(int button, Q3IconViewItem* item, const QPoint& pos);
+ void contextMenuRequested(Q3IconViewItem* item, const QPoint &pos);
+
+#ifndef QT_NO_DRAGANDDROP
+ void dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst);
+#endif
+ void moved();
+ void onItem(Q3IconViewItem *item);
+ void onViewport();
+ void itemRenamed(Q3IconViewItem *item, const QString &);
+ void itemRenamed(Q3IconViewItem *item);
+
+protected Q_SLOTS:
+ virtual void doAutoScroll();
+ virtual void adjustItems();
+ virtual void slotUpdate();
+
+private Q_SLOTS:
+ void movedContents(int dx, int dy);
+
+protected:
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ void contentsMousePressEvent(QMouseEvent *e);
+ void contentsMouseReleaseEvent(QMouseEvent *e);
+ void contentsMouseMoveEvent(QMouseEvent *e);
+ void contentsMouseDoubleClickEvent(QMouseEvent *e);
+ void contentsContextMenuEvent(QContextMenuEvent *e);
+
+#ifndef QT_NO_DRAGANDDROP
+ void contentsDragEnterEvent(QDragEnterEvent *e);
+ void contentsDragMoveEvent(QDragMoveEvent *e);
+ void contentsDragLeaveEvent(QDragLeaveEvent *e);
+ void contentsDropEvent(QDropEvent *e);
+#endif
+
+ void resizeEvent(QResizeEvent* e);
+ void keyPressEvent(QKeyEvent *e);
+ void focusInEvent(QFocusEvent *e);
+ void focusOutEvent(QFocusEvent *e);
+ void enterEvent(QEvent *e);
+
+ virtual void drawRubber(QPainter *p);
+#ifndef QT_NO_DRAGANDDROP
+ virtual Q3DragObject *dragObject();
+ virtual void startDrag();
+#endif
+ virtual void insertInGrid(Q3IconViewItem *item);
+ virtual void drawBackground(QPainter *p, const QRect &r);
+
+ void emitSelectionChanged(Q3IconViewItem * i = 0);
+ void emitRenamed(Q3IconViewItem *item);
+
+ Q3IconViewItem *makeRowLayout(Q3IconViewItem *begin, int &y, bool &changed);
+
+ void changeEvent(QEvent *);
+
+private:
+ Q_DISABLE_COPY(Q3IconView)
+
+ void contentsMousePressEventEx(QMouseEvent *e);
+ virtual void drawDragShapes(const QPoint &pnt);
+#ifndef QT_NO_DRAGANDDROP
+ virtual void initDragEnter(QDropEvent *e);
+#endif
+ Q3IconViewItem* findItemByName(Q3IconViewItem *start);
+ void handleItemChange(Q3IconViewItem *old, bool shift,
+ bool control, bool homeend = false);
+
+ int calcGridNum(int w, int x) const;
+ Q3IconViewItem *rowBegin(Q3IconViewItem *item) const;
+ void updateItemContainer(Q3IconViewItem *item);
+ void appendItemContainer();
+ void rebuildContainers();
+ enum Direction {
+ DirUp = 0,
+ DirDown,
+ DirLeft,
+ DirRight
+ };
+ Q3IconViewItem* findItem(Direction dir,
+ const QPoint &relativeTo,
+ const QRect &searchRect) const;
+ bool neighbourItem(Direction dir,
+ const QPoint &relativeTo,
+ const Q3IconViewItem *item) const;
+ QBitmap mask(QPixmap *pix) const;
+
+ Q3IconViewPrivate *d;
+};
+
+#endif // QT_NO_ICONVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QICONVIEW_H
diff --git a/src/qt3support/itemviews/q3listbox.cpp b/src/qt3support/itemviews/q3listbox.cpp
new file mode 100644
index 0000000..e0049f4
--- /dev/null
+++ b/src/qt3support/itemviews/q3listbox.cpp
@@ -0,0 +1,4687 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#if defined(Q_CC_BOR)
+// needed for qsort() because of a std namespace problem on Borland
+#include "qplatformdefs.h"
+#endif
+
+#include "q3listbox.h"
+#ifndef QT_NO_LISTBOX
+#include "qapplication.h"
+#include "qevent.h"
+#include "qfontmetrics.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qstringlist.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "qvector.h"
+#include "qpointer.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3ListBoxPrivate
+{
+public:
+ Q3ListBoxPrivate(Q3ListBox *lb):
+ head(0), last(0), cache(0), cacheIndex(-1), current(0),
+ highlighted(0), tmpCurrent(0), columnPos(1), rowPos(1), rowPosCache(0),
+ columnPosOne(0), rowMode(Q3ListBox::FixedNumber),
+ columnMode(Q3ListBox::FixedNumber), numRows(1), numColumns(1),
+ currentRow(0), currentColumn(0),
+ mousePressRow(-1), mousePressColumn(-1),
+ mouseMoveRow(-1), mouseMoveColumn(-1), mouseInternalPress(false),
+ scrollTimer(0), updateTimer(0), visibleTimer(0),
+ selectionMode(Q3ListBox::Single),
+ count(0),
+ listBox(lb), currInputString(QString()),
+ rowModeWins(false),
+ ignoreMoves(false),
+ layoutDirty(true),
+ mustPaintAll(true),
+ dragging(false),
+ dirtyDrag (false),
+ variableHeight(true /* !!! ### false */),
+ variableWidth(false),
+ inMenuMode(false)
+ {}
+ int findItemByName(int item, const QString &text);
+ ~Q3ListBoxPrivate();
+
+ Q3ListBoxItem * head, *last, *cache;
+ int cacheIndex;
+ Q3ListBoxItem * current, *highlighted, *tmpCurrent;
+
+ QVector<int> columnPos;
+ QVector<int> rowPos;
+ int rowPosCache;
+ int columnPosOne;
+
+ Q3ListBox::LayoutMode rowMode;
+ Q3ListBox::LayoutMode columnMode;
+ int numRows;
+ int numColumns;
+
+ int currentRow;
+ int currentColumn;
+ int mousePressRow;
+ int mousePressColumn;
+ int mouseMoveRow;
+ int mouseMoveColumn;
+ bool mouseInternalPress;
+
+ QTimer * scrollTimer;
+ QTimer * updateTimer;
+ QTimer * visibleTimer;
+ QTimer * resizeTimer;
+
+ QPoint scrollPos;
+
+ Q3ListBox::SelectionMode selectionMode;
+
+ int count;
+
+
+ Q3ListBox *listBox;
+ QString currInputString;
+ QTimer *inputTimer;
+
+ Q3ListBoxItem *pressedItem, *selectAnchor;
+
+ uint select :1;
+ uint pressedSelected :1;
+ uint rowModeWins :1;
+ uint ignoreMoves :1;
+ uint clearing :1;
+ uint layoutDirty :1;
+ uint mustPaintAll :1;
+ uint dragging :1;
+ uint dirtyDrag :1;
+ uint variableHeight :1;
+ uint variableWidth :1;
+ uint inMenuMode :1;
+
+ QRect *rubber;
+
+ struct SortableItem {
+ Q3ListBoxItem *item;
+ };
+};
+
+
+Q3ListBoxPrivate::~Q3ListBoxPrivate()
+{
+ Q_ASSERT(!head);
+}
+
+
+/*!
+ \class Q3ListBoxItem
+ \brief The Q3ListBoxItem class is the base class of all list box items.
+
+ \compat
+
+ This class is an abstract base class used for all list box items.
+ If you need to insert customized items into a Q3ListBox you must
+ inherit this class and reimplement paint(), height() and width().
+
+ \sa Q3ListBox
+*/
+
+/*!
+ Constructs an empty list box item in the list box \a listbox.
+*/
+
+Q3ListBoxItem::Q3ListBoxItem(Q3ListBox* listbox)
+{
+ lbox = listbox;
+ s = false;
+ dirty = true;
+ custom_highlight = false;
+ selectable = true;
+ p = n = 0;
+
+ if (listbox)
+ listbox->insertItem(this);
+}
+
+/*!
+ Constructs an empty list box item in the list box \a listbox and
+ inserts it after the item \a after or at the beginning if \a after
+ is 0.
+*/
+
+Q3ListBoxItem::Q3ListBoxItem(Q3ListBox* listbox, Q3ListBoxItem *after)
+{
+ lbox = listbox;
+ s = false;
+ dirty = true;
+ custom_highlight = false;
+ selectable = true;
+ p = n = 0;
+
+ if (listbox)
+ listbox->insertItem(this, after);
+}
+
+
+/*!
+ Destroys the list box item.
+*/
+
+Q3ListBoxItem::~Q3ListBoxItem()
+{
+ if (lbox)
+ lbox->takeItem(this);
+}
+
+
+/*!
+ Defines whether the list box item is responsible for drawing
+ itself in a highlighted state when being selected.
+
+ If \a b is false (the default), the list box will draw some
+ default highlight indicator before calling paint().
+
+ \sa isSelected(), paint()
+*/
+void Q3ListBoxItem::setCustomHighlighting(bool b)
+{
+ custom_highlight = b;
+}
+
+/*!
+ \fn void Q3ListBoxItem::paint(QPainter *p)
+
+ Implement this function to draw your item. The painter, \a p, is
+ already open for painting.
+
+ \sa height(), width()
+*/
+
+/*!
+ \fn int Q3ListBoxItem::width(const Q3ListBox* lb) const
+
+ Reimplement this function to return the width of your item. The \a
+ lb parameter is the same as listBox() and is provided for
+ convenience and compatibility.
+
+ The default implementation returns
+ \l{QApplication::globalStrut()}'s width.
+
+ \sa paint(), height()
+*/
+int Q3ListBoxItem::width(const Q3ListBox*) const
+{
+ return QApplication::globalStrut().width();
+}
+
+/*!
+ \fn int Q3ListBoxItem::height(const Q3ListBox* lb) const
+
+ Implement this function to return the height of your item. The \a
+ lb parameter is the same as listBox() and is provided for
+ convenience and compatibility.
+
+ The default implementation returns
+ \l{QApplication::globalStrut()}'s height.
+
+ \sa paint(), width()
+*/
+int Q3ListBoxItem::height(const Q3ListBox*) const
+{
+ return QApplication::globalStrut().height();
+}
+
+
+/*!
+ Returns the text of the item. This text is also used for sorting.
+
+ \sa setText()
+*/
+QString Q3ListBoxItem::text() const
+{
+ return txt;
+}
+
+/*!
+ Returns the pixmap associated with the item, or 0 if there isn't
+ one.
+
+ The default implementation returns 0.
+*/
+const QPixmap *Q3ListBoxItem::pixmap() const
+{
+ return 0;
+}
+
+/*! \fn void Q3ListBoxItem::setSelectable(bool b)
+
+ If \a b is true (the default) then this item can be selected by
+ the user; otherwise this item cannot be selected by the user.
+
+ \sa isSelectable()
+*/
+
+/*! \fn bool Q3ListBoxItem::isSelectable() const
+
+ Returns true if this item is selectable (the default); otherwise
+ returns false.
+
+ \sa setSelectable()
+*/
+
+
+/*!
+ \fn void Q3ListBoxItem::setText(const QString &text)
+
+ Sets the text of the Q3ListBoxItem to \a text. This \a text is also
+ used for sorting. The text is not shown unless explicitly drawn in
+ paint().
+
+ \sa text()
+*/
+
+
+/*!
+ \class Q3ListBoxText
+ \brief The Q3ListBoxText class provides list box items that display text.
+
+ \compat
+
+ The text is drawn in the widget's current font. If you need
+ several different fonts, you must implement your own subclass of
+ Q3ListBoxItem.
+
+ \sa Q3ListBox, Q3ListBoxItem
+*/
+
+
+/*!
+ Constructs a list box item in list box \a listbox showing the text
+ \a text.
+*/
+Q3ListBoxText::Q3ListBoxText(Q3ListBox *listbox, const QString &text)
+ :Q3ListBoxItem(listbox)
+{
+ setText(text);
+}
+
+/*!
+ Constructs a list box item showing the text \a text.
+*/
+
+Q3ListBoxText::Q3ListBoxText(const QString &text)
+ :Q3ListBoxItem()
+{
+ setText(text);
+}
+
+/*!
+ Constructs a list box item in list box \a listbox showing the text
+ \a text. The item is inserted after the item \a after, or at the
+ beginning if \a after is 0.
+*/
+
+Q3ListBoxText::Q3ListBoxText(Q3ListBox* listbox, const QString &text, Q3ListBoxItem *after)
+ : Q3ListBoxItem(listbox, after)
+{
+ setText(text);
+}
+
+/*!
+ Destroys the item.
+*/
+
+Q3ListBoxText::~Q3ListBoxText()
+{
+}
+
+/*!
+ Draws the text using \a painter.
+*/
+
+void Q3ListBoxText::paint(QPainter *painter)
+{
+ int itemHeight = height(listBox());
+ QFontMetrics fm = painter->fontMetrics();
+ int yPos = ((itemHeight - fm.height()) / 2) + fm.ascent();
+ painter->drawText(3, yPos, text());
+}
+
+/*!
+ Returns the height of a line of text in list box \a lb.
+
+ \sa paint(), width()
+*/
+
+int Q3ListBoxText::height(const Q3ListBox* lb) const
+{
+ int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0;
+ return qMax(h, QApplication::globalStrut().height());
+}
+
+/*!
+ Returns the width of this line in list box \a lb.
+
+ \sa paint(), height()
+*/
+
+int Q3ListBoxText::width(const Q3ListBox* lb) const
+{
+ int w = lb ? lb->fontMetrics().width(text()) + 6 : 0;
+ return qMax(w, QApplication::globalStrut().width());
+}
+
+/*!
+ \fn int Q3ListBoxText::rtti() const
+
+ \reimp
+
+ Returns 1.
+
+ Make your derived classes return their own values for rtti(), and
+ you can distinguish between listbox items. You should use values
+ greater than 1000 preferably a large random number, to allow for
+ extensions to this class.
+*/
+
+int Q3ListBoxText::rtti() const
+{
+ return RTTI;
+}
+
+/*!
+ \class Q3ListBoxPixmap
+ \brief The Q3ListBoxPixmap class provides list box items with a
+ pixmap and optional text.
+
+ \compat
+
+ Items of this class are drawn with the pixmap on the left with the
+ optional text to the right of the pixmap.
+
+ \sa Q3ListBox, Q3ListBoxItem
+*/
+
+
+/*!
+ Constructs a new list box item in list box \a listbox showing the
+ pixmap \a pixmap.
+*/
+
+Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &pixmap)
+ : Q3ListBoxItem(listbox)
+{
+ pm = pixmap;
+}
+
+/*!
+ Constructs a new list box item showing the pixmap \a pixmap.
+*/
+
+Q3ListBoxPixmap::Q3ListBoxPixmap(const QPixmap &pixmap)
+ : Q3ListBoxItem()
+{
+ pm = pixmap;
+}
+
+/*!
+ Constructs a new list box item in list box \a listbox showing the
+ pixmap \a pixmap. The item gets inserted after the item \a after,
+ or at the beginning if \a after is 0.
+*/
+
+Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &pixmap, Q3ListBoxItem *after)
+ : Q3ListBoxItem(listbox, after)
+{
+ pm = pixmap;
+}
+
+
+/*!
+ Destroys the item.
+*/
+
+Q3ListBoxPixmap::~Q3ListBoxPixmap()
+{
+}
+
+
+/*!
+ Constructs a new list box item in list box \a listbox showing the
+ pixmap \a pix and the text \a text.
+*/
+Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &pix, const QString& text)
+ : Q3ListBoxItem(listbox)
+{
+ pm = pix;
+ setText(text);
+}
+
+/*!
+ Constructs a new list box item showing the pixmap \a pix and the
+ text to \a text.
+*/
+Q3ListBoxPixmap::Q3ListBoxPixmap(const QPixmap & pix, const QString& text)
+ : Q3ListBoxItem()
+{
+ pm = pix;
+ setText(text);
+}
+
+/*!
+ Constructs a new list box item in list box \a listbox showing the
+ pixmap \a pix and the string \a text. The item gets inserted after
+ the item \a after, or at the beginning if \a after is 0.
+*/
+Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap & pix, const QString& text,
+ Q3ListBoxItem *after)
+ : Q3ListBoxItem(listbox, after)
+{
+ pm = pix;
+ setText(text);
+}
+
+/*!
+ \fn const QPixmap *Q3ListBoxPixmap::pixmap() const
+
+ Returns the pixmap associated with the item.
+*/
+
+
+/*!
+ Draws the pixmap using \a painter.
+*/
+
+void Q3ListBoxPixmap::paint(QPainter *painter)
+{
+ int itemHeight = height(listBox());
+ int yPos;
+
+ const QPixmap *pm = pixmap();
+ if (pm && ! pm->isNull()) {
+ yPos = (itemHeight - pm->height()) / 2;
+ painter->drawPixmap(3, yPos, *pm);
+ }
+
+ if (!text().isEmpty()) {
+ QFontMetrics fm = painter->fontMetrics();
+ yPos = ((itemHeight - fm.height()) / 2) + fm.ascent();
+ painter->drawText(pm->width() + 5, yPos, text());
+ }
+}
+
+/*!
+ Returns the height of the pixmap in list box \a lb.
+
+ \sa paint(), width()
+*/
+
+int Q3ListBoxPixmap::height(const Q3ListBox* lb) const
+{
+ int h;
+ if (text().isEmpty())
+ h = pm.height();
+ else
+ h = qMax(pm.height(), lb->fontMetrics().lineSpacing() + 2);
+ return qMax(h, QApplication::globalStrut().height());
+}
+
+/*!
+ Returns the width of the pixmap plus some margin in list box \a lb.
+
+ \sa paint(), height()
+*/
+
+int Q3ListBoxPixmap::width(const Q3ListBox* lb) const
+{
+ if (text().isEmpty())
+ return qMax(pm.width() + 6, QApplication::globalStrut().width());
+ return qMax(pm.width() + lb->fontMetrics().width(text()) + 6,
+ QApplication::globalStrut().width());
+}
+
+/*!
+ \fn int Q3ListBoxPixmap::rtti() const
+
+ \reimp
+
+ Returns 2.
+
+ Make your derived classes return their own values for rtti(), and
+ you can distinguish between listbox items. You should use values
+ greater than 1000 preferably a large random number, to allow for
+ extensions to this class.
+*/
+
+int Q3ListBoxPixmap::rtti() const
+{
+ return RTTI;
+}
+
+/*!
+ \class Q3ListBox
+ \brief The Q3ListBox widget provides a list of selectable, read-only items.
+
+ \compat
+
+ This is typically a single-column list in which either no item or
+ one item is selected, but it can also be used in many other ways.
+
+ Q3ListBox will add scroll bars as necessary, but it isn't intended
+ for \e really big lists. If you want more than a few thousand
+ items, it's probably better to use a different widget mainly
+ because the scroll bars won't provide very good navigation, but
+ also because Q3ListBox may become slow with huge lists. (See
+ Q3ListView and Q3Table for possible alternatives.)
+
+ There are a variety of selection modes described in the
+ Q3ListBox::SelectionMode documentation. The default is \l Single
+ selection mode, but you can change it using setSelectionMode().
+ (setMultiSelection() is still provided for compatibility with Qt
+ 1.x. We recommend using setSelectionMode() in all code.)
+
+ Because Q3ListBox offers multiple selection it must display
+ keyboard focus and selection state separately. Therefore there are
+ functions both to set the selection state of an item, i.e.
+ setSelected(), and to set which item displays keyboard focus, i.e.
+ setCurrentItem().
+
+ The list box normally arranges its items in a single column and
+ adds a vertical scroll bar if required. It is possible to have a
+ different fixed number of columns (setColumnMode()), or as many
+ columns as will fit in the list box's assigned screen space
+ (setColumnMode(FitToWidth)), or to have a fixed number of rows
+ (setRowMode()) or as many rows as will fit in the list box's
+ assigned screen space (setRowMode(FitToHeight)). In all these
+ cases Q3ListBox will add scroll bars, as appropriate, in at least
+ one direction.
+
+ If multiple rows are used, each row can be as high as necessary
+ (the normal setting), or you can request that all items will have
+ the same height by calling setVariableHeight(false). The same
+ applies to a column's width, see setVariableWidth().
+
+ The Q3ListBox's items are Q3ListBoxItem objects. Q3ListBox provides
+ methods to insert new items as strings, as pixmaps, and as
+ Q3ListBoxItem * (insertItem() with various arguments), and to
+ replace an existing item with a new string, pixmap or Q3ListBoxItem
+ (changeItem() with various arguments). You can also remove items
+ singly with removeItem() or clear() the entire list box. Note that
+ if you create a Q3ListBoxItem yourself and insert it, Q3ListBox
+ takes ownership of the item.
+
+ You can also create a Q3ListBoxItem, such as Q3ListBoxText or
+ Q3ListBoxPixmap, with the list box as first parameter. The item
+ will then append itself. When you delete an item it is
+ automatically removed from the list box.
+
+ The list of items can be arbitrarily large; Q3ListBox will add
+ scroll bars if necessary. Q3ListBox can display a single-column
+ (the common case) or multiple-columns, and offers both single and
+ multiple selection. Q3ListBox does not support multiple-column
+ items (but Q3ListView and Q3Table do), or tree hierarchies (but
+ Q3ListView does).
+
+ The list box items can be accessed both as Q3ListBoxItem objects
+ (recommended) and using integer indexes (the original Q3ListBox
+ implementation used an array of strings internally, and the API
+ still supports this mode of operation). Everything can be done
+ using the new objects, and most things can be done using indexes.
+
+ Each item in a Q3ListBox contains a Q3ListBoxItem. One of the items
+ can be the current item. The currentChanged() signal and the
+ highlighted() signal are emitted when a new item becomes current,
+ e.g. because the user clicks on it or Q3ListBox::setCurrentItem()
+ is called. The selected() signal is emitted when the user
+ double-clicks on an item or presses Enter on the current item.
+
+ If the user does not select anything, no signals are emitted and
+ currentItem() returns -1.
+
+ A list box has Qt::WheelFocus as a default focusPolicy(), i.e. it
+ can get keyboard focus by tabbing, clicking and through the use of
+ the mouse wheel.
+
+ New items can be inserted using insertItem(), insertStrList() or
+ insertStringList().
+
+ By default, vertical and horizontal scroll bars are added and
+ removed as necessary. setHScrollBarMode() and setVScrollBarMode()
+ can be used to change this policy.
+
+ If you need to insert types other than strings and pixmaps, you
+ must define new classes which inherit Q3ListBoxItem.
+
+ \warning The list box assumes ownership of all list box items and
+ will delete them when it does not need them any more.
+
+ \inlineimage qlistbox-m.png Screenshot in Motif style
+ \inlineimage qlistbox-w.png Screenshot in Windows style
+
+ \sa Q3ListView, QComboBox, QButtonGroup
+*/
+
+/*!
+ \enum Q3ListBox::SelectionMode
+
+ This enumerated type is used by Q3ListBox to indicate how it reacts
+ to selection by the user.
+
+ \value Single When the user selects an item, any already-selected
+ item becomes unselected and the user cannot unselect the selected
+ item. This means that the user can never clear the selection, even
+ though the selection may be cleared by the application programmer
+ using Q3ListBox::clearSelection().
+
+ \value Multi When the user selects an item the selection status
+ of that item is toggled and the other items are left alone.
+
+ \value Extended When the user selects an item the selection is
+ cleared and the new item selected. However, if the user presses
+ the Ctrl key when clicking on an item, the clicked item gets
+ toggled and all other items are left untouched. And if the user
+ presses the Shift key while clicking on an item, all items between
+ the current item and the clicked item get selected or unselected,
+ depending on the state of the clicked item. Also, multiple items
+ can be selected by dragging the mouse while the left mouse button
+ is kept pressed.
+
+ \value NoSelection Items cannot be selected.
+
+ In other words, \c Single is a real single-selection list box, \c
+ Multi is a real multi-selection list box, \c Extended is a list
+ box in which users can select multiple items but usually want to
+ select either just one or a range of contiguous items, and \c
+ NoSelection is for a list box where the user can look but not
+ touch.
+*/
+
+
+/*!
+ \enum Q3ListBox::LayoutMode
+
+ This enum type is used to specify how Q3ListBox lays out its rows
+ and columns.
+
+ \value FixedNumber There is a fixed number of rows (or columns).
+
+ \value FitToWidth There are as many columns as will fit
+ on-screen.
+
+ \value FitToHeight There are as many rows as will fit on-screen.
+
+ \value Variable There are as many rows as are required by the
+ column mode. (Or as many columns as required by the row mode.)
+
+ Example: When you call setRowMode(FitToHeight), columnMode()
+ automatically becomes \c Variable to accommodate the row mode
+ you've set.
+*/
+
+/*!
+ \fn void Q3ListBox::onItem(Q3ListBoxItem *i)
+
+ This signal is emitted when the user moves the mouse cursor onto
+ an item, similar to the QWidget::enterEvent() function. \a i is
+ the Q3ListBoxItem that the mouse has moved on.
+*/
+
+// ### bug here too? enter/leave event may noit considered. move the
+// mouse out of the window and back in, to the same item - does it
+// work?
+
+/*!
+ \fn void Q3ListBox::onViewport()
+
+ This signal is emitted when the user moves the mouse cursor from
+ an item to an empty part of the list box.
+*/
+
+
+/*!
+ Constructs a new empty list box called \a name and with parent \a
+ parent and widget attributes \a f.
+
+ This constructor sets the Qt::WA_StaticContent and the
+ Qt::WA_NoBackground attributes to boost performance when drawing
+ Q3ListBoxItems. This may be unsuitable for custom Q3ListBoxItem
+ classes, in which case Qt::WA_StaticContents and Qt::WA_NoBackground
+ should be cleared on the viewport() after construction.
+*/
+
+Q3ListBox::Q3ListBox(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : Q3ScrollView(parent, name, f | Qt::WStaticContents | Qt::WNoAutoErase)
+{
+ d = new Q3ListBoxPrivate(this);
+ d->updateTimer = new QTimer(this, "listbox update timer");
+ d->visibleTimer = new QTimer(this, "listbox visible timer");
+ d->inputTimer = new QTimer(this, "listbox input timer");
+ d->resizeTimer = new QTimer(this, "listbox resize timer");
+ d->clearing = false;
+ d->pressedItem = 0;
+ d->selectAnchor = 0;
+ d->select = false;
+ d->rubber = 0;
+
+ setMouseTracking(true);
+ viewport()->setMouseTracking(true);
+
+ connect(d->updateTimer, SIGNAL(timeout()),
+ this, SLOT(refreshSlot()));
+ connect(d->visibleTimer, SIGNAL(timeout()),
+ this, SLOT(ensureCurrentVisible()));
+ connect(d->resizeTimer, SIGNAL(timeout()),
+ this, SLOT(adjustItems()));
+ viewport()->setBackgroundRole(QPalette::Base);
+ viewport()->setFocusProxy(this);
+ viewport()->setFocusPolicy(Qt::WheelFocus);
+ setFocusPolicy(Qt::WheelFocus);
+ setAttribute(Qt::WA_MacShowFocusRect);
+}
+
+
+Q3ListBox * Q3ListBox::changedListBox = 0;
+
+/*!
+ Destroys the list box. Deletes all list box items.
+*/
+
+Q3ListBox::~Q3ListBox()
+{
+ if (changedListBox == this)
+ changedListBox = 0;
+ clear();
+ delete d;
+ d = 0;
+}
+
+/*!
+ \fn void Q3ListBox::pressed(Q3ListBoxItem *item)
+
+ This signal is emitted when the user presses any mouse button. If
+ \a item is not 0, the cursor is on \a item. If \a item is 0, the
+ mouse cursor isn't on any item.
+
+ Note that you must not delete any Q3ListBoxItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListBox::pressed(Q3ListBoxItem *item, const QPoint &pnt)
+ \overload
+
+ This signal is emitted when the user presses any mouse button. If
+ \a item is not 0, the cursor is on \a item. If \a item is 0, the
+ mouse cursor isn't on any item.
+
+ \a pnt is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()).
+
+ Note that you must not delete any Q3ListBoxItem objects in slots
+ connected to this signal.
+
+ \sa mouseButtonPressed() rightButtonPressed() clicked()
+*/
+
+/*!
+ \fn void Q3ListBox::clicked(Q3ListBoxItem *item)
+
+ This signal is emitted when the user clicks any mouse button. If
+ \a item is not 0, the cursor is on \a item. If \a item is 0, the
+ mouse cursor isn't on any item.
+
+ Note that you must not delete any Q3ListBoxItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListBox::clicked(Q3ListBoxItem *item, const QPoint &pnt)
+ \overload
+
+ This signal is emitted when the user clicks any mouse button. If
+ \a item is not 0, the cursor is on \a item. If \a item is 0, the
+ mouse cursor isn't on any item.
+
+ \a pnt is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()). (If the click's
+ press and release differs by a pixel or two, \a pnt is the
+ position at release time.)
+
+ Note that you must not delete any Q3ListBoxItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListBox::mouseButtonClicked (int button, Q3ListBoxItem * item, const QPoint & pos)
+
+ This signal is emitted when the user clicks mouse button \a
+ button. If \a item is not 0, the cursor is on \a item. If \a item
+ is 0, the mouse cursor isn't on any item.
+
+ \a pos is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()). (If the click's
+ press and release differs by a pixel or two, \a pos is the
+ position at release time.)
+
+ Note that you must not delete any Q3ListBoxItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListBox::mouseButtonPressed (int button, Q3ListBoxItem * item, const QPoint & pos)
+
+ This signal is emitted when the user presses mouse button \a
+ button. If \a item is not 0, the cursor is on \a item. If \a item
+ is 0, the mouse cursor isn't on any item.
+
+ \a pos is the position of the mouse cursor in the global
+ coordinate system (QMouseEvent::globalPos()).
+
+ Note that you must not delete any Q3ListBoxItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListBox::doubleClicked(Q3ListBoxItem *item)
+
+ This signal is emitted whenever an item is double-clicked. It's
+ emitted on the second button press, not the second button release.
+ If \a item is not 0, the cursor is on \a item. If \a item is 0,
+ the mouse cursor isn't on any item.
+*/
+
+
+/*!
+ \fn void Q3ListBox::returnPressed(Q3ListBoxItem *item)
+
+ This signal is emitted when Enter or Return is pressed. The
+ \a item passed in the argument is currentItem().
+*/
+
+/*!
+ \fn void Q3ListBox::rightButtonClicked(Q3ListBoxItem *item, const QPoint& point)
+
+ This signal is emitted when the right button is clicked. The \a
+ item is the item that the button was clicked on (which could be
+ 0 if no item was clicked on), and the \a point is where the
+ click took place in global coordinates.
+*/
+
+
+/*!
+ \fn void Q3ListBox::rightButtonPressed (Q3ListBoxItem *item, const QPoint &point)
+
+ This signal is emitted when the right button is pressed. The \a
+ item is the item that the button was pressed over (which could be
+ 0 if no item was pressed over), and the \a point is where the
+ press took place in global coordinates.
+*/
+
+/*!
+ \fn void Q3ListBox::contextMenuRequested(Q3ListBoxItem *item, const QPoint & pos)
+
+ This signal is emitted when the user invokes a context menu with
+ the right mouse button or with special system keys, with \a item
+ being the item under the mouse cursor or the current item,
+ respectively.
+
+ \a pos is the position for the context menu in the global
+ coordinate system.
+*/
+
+/*!
+ \fn void Q3ListBox::selectionChanged()
+
+ This signal is emitted when the selection set of a list box
+ changes. This signal is emitted in each selection mode. If the
+ user selects five items by drag-selecting, Q3ListBox tries to emit
+ just one selectionChanged() signal so the signal can be connected
+ to computationally expensive slots.
+
+ \sa selected() currentItem()
+*/
+
+/*!
+ \fn void Q3ListBox::selectionChanged(Q3ListBoxItem *item)
+ \overload
+
+ This signal is emitted when the selection in a \l Single selection
+ list box changes. \a item is the newly selected list box item.
+
+ \sa selected() currentItem()
+*/
+
+/*!
+ \fn void Q3ListBox::currentChanged(Q3ListBoxItem *item)
+
+ This signal is emitted when the user makes a new item the current
+ item. \a item is the new current list box item.
+
+ \sa setCurrentItem() currentItem()
+*/
+
+/*!
+ \fn void Q3ListBox::highlighted(int index)
+
+ This signal is emitted when the user makes a new item the current
+ item. \a index is the index of the new current item.
+
+ \sa currentChanged() selected() currentItem() selectionChanged()
+*/
+
+/*!
+ \fn void Q3ListBox::highlighted(Q3ListBoxItem *item)
+
+ \overload
+
+ This signal is emitted when the user makes a new \a item the current
+ \a item.
+
+ \sa currentChanged() selected() currentItem() selectionChanged()
+*/
+
+/*!
+ \fn void Q3ListBox::highlighted(const QString & text)
+
+ \overload
+
+ This signal is emitted when the user makes a new item the current
+ item and the item is (or has) as string. The argument is the new
+ current item's \a text.
+
+ \sa currentChanged() selected() currentItem() selectionChanged()
+*/
+
+/*!
+ \fn void Q3ListBox::selected(int index)
+
+ This signal is emitted when the user double-clicks on an item or
+ presses Enter on the current item. \a index is the index of the
+ selected item.
+
+ \sa currentChanged() highlighted() selectionChanged()
+*/
+
+/*!
+ \fn void Q3ListBox::selected(Q3ListBoxItem *item)
+
+ \overload
+
+ This signal is emitted when the user double-clicks on an \a item or
+ presses Enter on the current \a item.
+
+ \sa currentChanged() highlighted() selectionChanged()
+*/
+
+/*!
+ \fn void Q3ListBox::selected(const QString &text)
+
+ \overload
+
+ This signal is emitted when the user double-clicks on an item or
+ presses Enter on the current item, and the item is (or has) a
+ string. The argument is the \a text of the selected item.
+
+ \sa currentChanged() highlighted() selectionChanged()
+*/
+
+/*!
+ \property Q3ListBox::count
+ \brief the number of items in the list box
+*/
+
+uint Q3ListBox::count() const
+{
+ return d->count;
+}
+
+#if 0
+/*!
+ Inserts the string list \a list into the list at position \a
+ index.
+
+ If \a index is negative, \a list is inserted at the end of the
+ list. If \a index is too large, the operation is ignored.
+
+ \warning This function uses \c{const char *} rather than QString,
+ so we recommend against using it. It is provided so that legacy
+ code will continue to work, and so that programs that certainly
+ will not need to handle code outside a single 8-bit locale can use
+ it. See insertStringList() which uses real QStrings.
+
+ \warning This function is never significantly faster than a loop
+ around insertItem().
+
+ \sa insertItem(), insertStringList()
+*/
+
+void Q3ListBox::insertStrList(const QStrList *list, int index)
+{
+ if (!list) {
+ Q_ASSERT(list != 0);
+ return;
+ }
+ insertStrList(*list, index);
+}
+#endif
+
+
+/*!
+ Inserts the string list \a list into the list at position \a
+ index.
+
+ If \a index is negative, \a list is inserted at the end of the
+ list. If \a index is too large, the operation is ignored.
+
+ \warning This function is never significantly faster than a loop
+ around insertItem().
+
+ \sa insertItem(), insertStrList()
+*/
+
+void Q3ListBox::insertStringList(const QStringList & list, int index)
+{
+ if (index < 0)
+ index = count();
+ for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ insertItem(new Q3ListBoxText(*it), index++);
+}
+
+
+#if 0
+/*!
+ \overload
+
+ Inserts the string list \a list into the list at position \a
+ index.
+
+ If \a index is negative, \a list is inserted at the end of the
+ list. If \a index is too large, the operation is ignored.
+
+ \warning This function uses \c{const char *} rather than QString,
+ so we recommend against using it. It is provided so that legacy
+ code will continue to work, and so that programs that certainly
+ will not need to handle code outside a single 8-bit locale can use
+ it. See insertStringList() which uses real QStrings.
+
+ \warning This function is never significantly faster than a loop
+ around insertItem().
+
+ \sa insertItem(), insertStringList()
+*/
+void Q3ListBox::insertStrList(const QStrList & list, int index)
+{
+ QStrListIterator it(list);
+ const char* txt;
+ if (index < 0)
+ index = count();
+ while ((txt=it.current())) {
+ ++it;
+ insertItem(new Q3ListBoxText(QString::fromLatin1(txt)),
+ index++);
+ }
+ if (hasFocus() && !d->current)
+ setCurrentItem(d->head);
+}
+#endif
+
+
+/*!
+ Inserts the \a numStrings strings of the array \a strings into the
+ list at position \a index.
+
+ If \a index is negative, insertStrList() inserts \a strings at the
+ end of the list. If \a index is too large, the operation is
+ ignored.
+
+ \warning This function uses \c{const char *} rather than QString,
+ so we recommend against using it. It is provided so that legacy
+ code will continue to work, and so that programs that certainly
+ will not need to handle code outside a single 8-bit locale can use
+ it. See insertStringList() which uses real QStrings.
+
+ \warning This function is never significantly faster than a loop
+ around insertItem().
+
+ \sa insertItem(), insertStringList()
+*/
+
+void Q3ListBox::insertStrList(const char **strings, int numStrings, int index)
+{
+ if (!strings) {
+ Q_ASSERT(strings != 0);
+ return;
+ }
+ if (index < 0)
+ index = count();
+ int i = 0;
+ while ((numStrings<0 && strings[i]!=0) || i<numStrings) {
+ insertItem(new Q3ListBoxText(QString::fromLatin1(strings[i])),
+ index + i);
+ i++;
+ }
+ if (hasFocus() && !d->current)
+ setCurrentItem(d->head);
+}
+
+/*!
+ Inserts the item \a lbi into the list at position \a index.
+
+ If \a index is negative or larger than the number of items in the
+ list box, \a lbi is inserted at the end of the list.
+
+ \sa insertStrList()
+*/
+
+void Q3ListBox::insertItem(const Q3ListBoxItem *lbi, int index)
+{
+ if (!lbi)
+ return;
+
+ if (index < 0)
+ index = d->count;
+
+ if (index >= d->count) {
+ insertItem(lbi, d->last);
+ return;
+ }
+
+ Q3ListBoxItem * item = (Q3ListBoxItem *)lbi;
+ d->count++;
+ d->cache = 0;
+
+ item->lbox = this;
+ if (!d->head || index == 0) {
+ item->n = d->head;
+ item->p = 0;
+ d->head = item;
+ item->dirty = true;
+ if (item->n)
+ item->n->p = item;
+ } else {
+ Q3ListBoxItem * i = d->head;
+ while (i->n && index > 1) {
+ i = i->n;
+ index--;
+ }
+ if (i->n) {
+ item->n = i->n;
+ item->p = i;
+ item->n->p = item;
+ item->p->n = item;
+ } else {
+ i->n = item;
+ item->p = i;
+ item->n = 0;
+ }
+ }
+
+ if (hasFocus() && !d->current) {
+ d->current = d->head;
+ updateItem(d->current);
+ emit highlighted(d->current);
+ emit highlighted(d->current->text());
+ emit highlighted(index);
+ }
+
+ triggerUpdate(true);
+}
+
+/*!
+ \overload
+
+ Inserts the item \a lbi into the list after the item \a after, or
+ at the beginning if \a after is 0.
+
+ \sa insertStrList()
+*/
+
+void Q3ListBox::insertItem(const Q3ListBoxItem *lbi, const Q3ListBoxItem *after)
+{
+ if (!lbi)
+ return;
+
+ Q3ListBoxItem * item = (Q3ListBoxItem*)lbi;
+ d->count++;
+ d->cache = 0;
+
+ item->lbox = this;
+ if (!d->head || !after) {
+ item->n = d->head;
+ item->p = 0;
+ d->head = item;
+ item->dirty = true;
+ if (item->n)
+ item->n->p = item;
+ } else {
+ Q3ListBoxItem * i = (Q3ListBoxItem*) after;
+ if (i) {
+ item->n = i->n;
+ item->p = i;
+ if (item->n)
+ item->n->p = item;
+ if (item->p)
+ item->p->n = item;
+ }
+ }
+
+ if (after == d->last)
+ d->last = (Q3ListBoxItem*) lbi;
+
+ if (hasFocus() && !d->current) {
+ d->current = d->head;
+ updateItem(d->current);
+ emit highlighted(d->current);
+ emit highlighted(d->current->text());
+ emit highlighted(index(d->current));
+ }
+
+ triggerUpdate(true);
+}
+
+/*!
+ \overload
+
+ Inserts a new list box text item with the text \a text into the
+ list at position \a index.
+
+ If \a index is negative, \a text is inserted at the end of the
+ list.
+
+ \sa insertStrList()
+*/
+
+void Q3ListBox::insertItem(const QString &text, int index)
+{
+ insertItem(new Q3ListBoxText(text), index);
+}
+
+/*!
+ \overload
+
+ Inserts a new list box pixmap item with the pixmap \a pixmap into
+ the list at position \a index.
+
+ If \a index is negative, \a pixmap is inserted at the end of the
+ list.
+
+ \sa insertStrList()
+*/
+
+void Q3ListBox::insertItem(const QPixmap &pixmap, int index)
+{
+ insertItem(new Q3ListBoxPixmap(pixmap), index);
+}
+
+/*!
+ \overload
+
+ Inserts a new list box pixmap item with the pixmap \a pixmap and
+ the text \a text into the list at position \a index.
+
+ If \a index is negative, \a pixmap is inserted at the end of the
+ list.
+
+ \sa insertStrList()
+*/
+
+void Q3ListBox::insertItem(const QPixmap &pixmap, const QString &text, int index)
+{
+ insertItem(new Q3ListBoxPixmap(pixmap, text), index);
+}
+
+/*!
+ Removes and deletes the item at position \a index. If \a index is
+ equal to currentItem(), a new item becomes current and the
+ currentChanged() and highlighted() signals are emitted.
+
+ \sa insertItem(), clear()
+*/
+
+void Q3ListBox::removeItem(int index)
+{
+ bool wasVisible = itemVisible(currentItem());
+ delete item(index);
+ triggerUpdate(true);
+ if (wasVisible)
+ ensureCurrentVisible();
+}
+
+
+/*!
+ Deletes all the items in the list.
+
+ \sa removeItem()
+*/
+
+void Q3ListBox::clear()
+{
+ setContentsPos(0, 0);
+ bool blocked = signalsBlocked();
+ blockSignals(true);
+ d->clearing = true;
+ d->current = 0;
+ d->tmpCurrent = 0;
+ Q3ListBoxItem * i = d->head;
+ d->head = 0;
+ while (i) {
+ Q3ListBoxItem * n = i->n;
+ i->n = i->p = 0;
+ delete i;
+ i = n;
+ }
+ d->count = 0;
+ d->numRows = 1;
+ d->numColumns = 1;
+ d->currentRow = 0;
+ d->currentColumn = 0;
+ d->mousePressRow = -1;
+ d->mousePressColumn = -1;
+ d->mouseMoveRow = -1;
+ d->mouseMoveColumn = -1;
+ clearSelection();
+ d->selectAnchor = 0;
+ blockSignals(blocked);
+ triggerUpdate(true);
+ d->last = 0;
+ d->clearing = false;
+}
+
+
+/*!
+ Returns the text at position \a index, or an empty string if there
+ is no text at that position.
+
+ \sa pixmap()
+*/
+
+QString Q3ListBox::text(int index) const
+{
+ Q3ListBoxItem * i = item(index);
+ if (i)
+ return i->text();
+ return QString();
+}
+
+
+/*!
+ Returns a pointer to the pixmap at position \a index, or 0 if
+ there is no pixmap there.
+
+ \sa text()
+*/
+
+const QPixmap *Q3ListBox::pixmap(int index) const
+{
+ Q3ListBoxItem * i = item(index);
+ if (i)
+ return i->pixmap();
+ return 0;
+}
+
+/*!
+ \overload
+
+ Replaces the item at position \a index with a new list box text
+ item with text \a text.
+
+ The operation is ignored if \a index is out of range.
+
+ \sa insertItem(), removeItem()
+*/
+
+void Q3ListBox::changeItem(const QString &text, int index)
+{
+ if(index >= 0 && index < (int)count())
+ changeItem(new Q3ListBoxText(text), index);
+}
+
+/*!
+ \overload
+
+ Replaces the item at position \a index with a new list box pixmap
+ item with pixmap \a pixmap.
+
+ The operation is ignored if \a index is out of range.
+
+ \sa insertItem(), removeItem()
+*/
+
+void Q3ListBox::changeItem(const QPixmap &pixmap, int index)
+{
+ if(index >= 0 && index < (int)count())
+ changeItem(new Q3ListBoxPixmap(pixmap), index);
+}
+
+/*!
+ \overload
+
+ Replaces the item at position \a index with a new list box pixmap
+ item with pixmap \a pixmap and text \a text.
+
+ The operation is ignored if \a index is out of range.
+
+ \sa insertItem(), removeItem()
+*/
+
+void Q3ListBox::changeItem(const QPixmap &pixmap, const QString &text, int index)
+{
+ if(index >= 0 && index < (int)count())
+ changeItem(new Q3ListBoxPixmap(pixmap, text), index);
+}
+
+
+
+/*!
+ Replaces the item at position \a index with \a lbi. If \a index is
+ negative or too large, changeItem() does nothing.
+
+ The item that has been changed will become selected.
+
+ \sa insertItem(), removeItem()
+*/
+
+void Q3ListBox::changeItem(const Q3ListBoxItem *lbi, int index)
+{
+ if (!lbi || index < 0 || index >= (int)count())
+ return;
+
+ removeItem(index);
+ insertItem(lbi, index);
+ setCurrentItem(index);
+}
+
+
+/*!
+ \property Q3ListBox::numItemsVisible
+ \brief the number of visible items.
+
+ Both partially and entirely visible items are counted.
+*/
+
+int Q3ListBox::numItemsVisible() const
+{
+ doLayout();
+
+ int columns = 0;
+
+ int x = contentsX();
+ int i=0;
+ while (i < (int)d->columnPos.size()-1 &&
+ d->columnPos[i] < x)
+ i++;
+ if (i < (int)d->columnPos.size()-1 &&
+ d->columnPos[i] > x)
+ columns++;
+ x += visibleWidth();
+ while (i < (int)d->columnPos.size()-1 &&
+ d->columnPos[i] < x) {
+ i++;
+ columns++;
+ }
+
+ int y = contentsY();
+ int rows = 0;
+ while (i < (int)d->rowPos.size()-1 &&
+ d->rowPos[i] < y)
+ i++;
+ if (i < (int)d->rowPos.size()-1 &&
+ d->rowPos[i] > y)
+ rows++;
+ y += visibleHeight();
+ while (i < (int)d->rowPos.size()-1 &&
+ d->rowPos[i] < y) {
+ i++;
+ rows++;
+ }
+
+ return rows*columns;
+}
+
+int Q3ListBox::currentItem() const
+{
+ if (!d->current || !d->head)
+ return -1;
+
+ return index(d->current);
+}
+
+
+/*!
+ \property Q3ListBox::currentText
+ \brief the text of the current item.
+
+ This is equivalent to text(currentItem()).
+*/
+
+
+/*!
+ \property Q3ListBox::currentItem
+ \brief the current highlighted item
+
+ When setting this property, the highlighting is moved to the item
+ and the list box scrolled as necessary.
+
+ If no item is current, currentItem() returns -1.
+*/
+
+void Q3ListBox::setCurrentItem(int index)
+{
+ setCurrentItem(item(index));
+}
+
+/*!
+ \reimp
+*/
+QVariant Q3ListBox::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ if (query == Qt::ImMicroFocus)
+ return d->current ? itemRect(d->current) : QRect();
+ return QWidget::inputMethodQuery(query);
+}
+
+/*!
+ \overload
+
+ Sets the current item to the Q3ListBoxItem \a i.
+*/
+void Q3ListBox::setCurrentItem(Q3ListBoxItem * i)
+{
+ if (!i || d->current == i)
+ return;
+
+ Q3ListBoxItem * o = d->current;
+ d->current = i;
+ int ind = index(i);
+
+ if (i && selectionMode() == Single) {
+ bool changed = false;
+ if (o && o->s) {
+ changed = true;
+ o->s = false;
+ }
+ if (i && !i->s && d->selectionMode != NoSelection && i->isSelectable()) {
+ i->s = true;
+ changed = true;
+ emit selectionChanged(i);
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged);
+#endif
+ }
+ if (changed) {
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+#endif
+ }
+ }
+
+ d->currentColumn = ind / numRows();
+ d->currentRow = ind % numRows();
+ if (o)
+ updateItem(o);
+ if (i)
+ updateItem(i);
+ // scroll after the items are redrawn
+ d->visibleTimer->start(1, true);
+
+ QString tmp;
+ if (i)
+ tmp = i->text();
+ emit highlighted(i);
+ if (!tmp.isNull())
+ emit highlighted(tmp);
+ emit highlighted(ind);
+ emit currentChanged(i);
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::Focus);
+#endif
+}
+
+
+/*!
+ Returns a pointer to the item at position \a index, or 0 if \a
+ index is out of bounds.
+
+ \sa index()
+*/
+
+Q3ListBoxItem *Q3ListBox::item(int index) const
+{
+ if (index < 0 || index > d->count -1)
+ return 0;
+
+ Q3ListBoxItem * i = d->head;
+
+ if (d->cache && index > 0) {
+ i = d->cache;
+ int idx = d->cacheIndex;
+ while (i && idx < index) {
+ idx++;
+ i = i->n;
+ }
+ while (i && idx > index) {
+ idx--;
+ i = i->p;
+ }
+ } else {
+ int idx = index;
+ while (i && idx > 0) {
+ idx--;
+ i = i->n;
+ }
+ }
+
+ if (index > 0) {
+ d->cache = i;
+ d->cacheIndex = index;
+ }
+
+ return i;
+}
+
+
+/*!
+ Returns the index of \a lbi, or -1 if the item is not in this list
+ box or \a lbi is 0.
+
+ \sa item()
+*/
+
+int Q3ListBox::index(const Q3ListBoxItem * lbi) const
+{
+ if (!lbi)
+ return -1;
+ Q3ListBoxItem * i_n = d->head;
+ int c_n = 0;
+ if (d->cache) {
+ i_n = d->cache;
+ c_n = d->cacheIndex;
+ }
+ Q3ListBoxItem* i_p = i_n;
+ int c_p = c_n;
+ while ((i_n != 0 || i_p != 0) && i_n != lbi && i_p != lbi) {
+ if (i_n) {
+ c_n++;
+ i_n = i_n->n;
+ }
+ if (i_p) {
+ c_p--;
+ i_p = i_p->p;
+ }
+ }
+ if (i_p == lbi)
+ return c_p;
+ if (i_n == lbi)
+ return c_n;
+ return -1;
+}
+
+
+
+/*!
+ Returns true if the item at position \a index is at least partly
+ visible; otherwise returns false.
+*/
+
+bool Q3ListBox::itemVisible(int index)
+{
+ Q3ListBoxItem * i = item(index);
+ return i ? itemVisible(i) : false;
+}
+
+
+/*!
+ \overload
+
+ Returns true if \a item is at least partly visible; otherwise
+ returns false.
+*/
+
+bool Q3ListBox::itemVisible(const Q3ListBoxItem * item)
+{
+ if (d->layoutDirty)
+ doLayout();
+
+ int i = index(item);
+ int col = i / numRows();
+ int row = i % numRows();
+ return (d->columnPos[col] < contentsX()+visibleWidth() &&
+ d->rowPos[row] < contentsY()+visibleHeight() &&
+ d->columnPos[col+1] > contentsX() &&
+ d->rowPos[row+1] > contentsY());
+}
+
+
+/*! \reimp */
+
+void Q3ListBox::mousePressEvent(QMouseEvent *e)
+{
+ mousePressEventEx(e);
+}
+
+void Q3ListBox::mousePressEventEx(QMouseEvent *e)
+{
+ d->mouseInternalPress = true;
+ Q3ListBoxItem * i = itemAt(e->pos());
+
+ if (!i && !d->current && d->head) {
+ d->current = d->head;
+ updateItem(d->head);
+ }
+
+ if (!i && (d->selectionMode != Single || e->button() == Qt::RightButton)
+ && !(e->state() & Qt::ControlButton))
+ clearSelection();
+
+ d->select = d->selectionMode == Multi ? (i ? !i->isSelected() : false) : true;
+ d->pressedSelected = i && i->s;
+
+ if (i)
+ d->selectAnchor = i;
+ if (i) {
+ switch(selectionMode()) {
+ default:
+ case Single:
+ if (!i->s || i != d->current) {
+ if (i->isSelectable())
+ setSelected(i, true);
+ else
+ setCurrentItem(i);
+ }
+ break;
+ case Extended:
+ if (i) {
+ bool shouldBlock = false;
+ if (!(e->state() & Qt::ShiftButton) &&
+ !(e->state() & Qt::ControlButton)) {
+ if (!i->isSelected()) {
+ bool b = signalsBlocked();
+ blockSignals(true);
+ clearSelection();
+ blockSignals(b);
+ }
+ setSelected(i, true);
+ d->dragging = true; // always assume dragging
+ shouldBlock = true;
+ } else if (e->state() & Qt::ShiftButton) {
+ d->pressedSelected = false;
+ Q3ListBoxItem *oldCurrent = item(currentItem());
+ bool down = index(oldCurrent) < index(i);
+
+ Q3ListBoxItem *lit = down ? oldCurrent : i;
+ bool select = d->select;
+ bool blocked = signalsBlocked();
+ blockSignals(true);
+ for (;; lit = lit->n) {
+ if (!lit) {
+ triggerUpdate(false);
+ break;
+ }
+ if (down && lit == i) {
+ setSelected(i, select);
+ triggerUpdate(false);
+ break;
+ }
+ if (!down && lit == oldCurrent) {
+ setSelected(oldCurrent, select);
+ triggerUpdate(false);
+ break;
+ }
+ setSelected(lit, select);
+ }
+ blockSignals(blocked);
+ emit selectionChanged();
+ } else if (e->state() & Qt::ControlButton) {
+ setSelected(i, !i->isSelected());
+ shouldBlock = true;
+ d->pressedSelected = false;
+ }
+ bool blocked = signalsBlocked();
+ blockSignals(shouldBlock);
+ setCurrentItem(i);
+ blockSignals(blocked);
+ }
+ break;
+ case Multi:
+ {
+ setSelected(i, !i->s);
+ bool b = signalsBlocked();
+ blockSignals(true);
+ setCurrentItem(i);
+ blockSignals(b);
+ break;
+ }
+ case NoSelection:
+ setCurrentItem(i);
+ break;
+ }
+ } else {
+ bool unselect = true;
+ if (e->button() == Qt::LeftButton) {
+ if (d->selectionMode == Multi ||
+ d->selectionMode == Extended) {
+ d->tmpCurrent = d->current;
+ d->current = 0;
+ updateItem(d->tmpCurrent);
+ if (d->rubber)
+ delete d->rubber;
+ d->rubber = 0;
+ d->rubber = new QRect(e->x(), e->y(), 0, 0);
+
+ if (d->selectionMode == Extended && !(e->state() & Qt::ControlButton))
+ selectAll(false);
+ unselect = false;
+ }
+ if (unselect && (e->button() == Qt::RightButton ||
+ (selectionMode() == Multi || selectionMode() == Extended)))
+ clearSelection();
+ }
+ }
+
+ // for sanity, in case people are event-filtering or whatnot
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ if (i) {
+ d->mousePressColumn = d->currentColumn;
+ d->mousePressRow = d->currentRow;
+ } else {
+ d->mousePressColumn = -1;
+ d->mousePressRow = -1;
+ }
+ d->ignoreMoves = false;
+
+ d->pressedItem = i;
+
+ emit pressed(i);
+ emit pressed(i, e->globalPos());
+ emit mouseButtonPressed(e->button(), i, e->globalPos());
+ if (e->button() == Qt::RightButton)
+ emit rightButtonPressed(i, e->globalPos());
+}
+
+
+/*! \reimp */
+
+void Q3ListBox::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (d->selectionMode == Extended &&
+ d->dragging) {
+ d->dragging = false;
+ if (d->current != d->pressedItem) {
+ updateSelection(); // when we drag, we get an update after we release
+ }
+ }
+
+ if (d->rubber) {
+ drawRubber();
+ delete d->rubber;
+ d->rubber = 0;
+ d->current = d->tmpCurrent;
+ updateItem(d->current);
+ }
+ if (d->scrollTimer)
+ mouseMoveEvent(e);
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ d->ignoreMoves = false;
+
+ if (d->selectionMode == Extended &&
+ d->current == d->pressedItem &&
+ d->pressedSelected && d->current) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ clearSelection();
+ blockSignals(block);
+ d->current->s = true;
+ emit selectionChanged();
+ }
+
+ Q3ListBoxItem * i = itemAt(e->pos());
+ bool emitClicked = (d->mousePressColumn != -1 && d->mousePressRow != -1) || !d->pressedItem;
+ emitClicked = emitClicked && d->pressedItem == i;
+ d->pressedItem = 0;
+ d->mousePressRow = -1;
+ d->mousePressColumn = -1;
+ d->mouseInternalPress = false;
+ if (emitClicked) {
+ emit clicked(i);
+ emit clicked(i, e->globalPos());
+ emit mouseButtonClicked(e->button(), i, e->globalPos());
+ if (e->button() == Qt::RightButton)
+ emit rightButtonClicked(i, e->globalPos());
+ }
+}
+
+
+/*! \reimp */
+
+void Q3ListBox::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ bool ok = true;
+ Q3ListBoxItem *i = itemAt(e->pos());
+ if (!i || selectionMode() == NoSelection)
+ ok = false;
+
+ d->ignoreMoves = true;
+
+ if (d->current && ok) {
+ Q3ListBoxItem * i = d->current;
+ QString tmp = d->current->text();
+ emit selected(currentItem());
+ emit selected(i);
+ if (!tmp.isNull())
+ emit selected(tmp);
+ emit doubleClicked(i);
+ }
+}
+
+
+/*! \reimp */
+
+void Q3ListBox::mouseMoveEvent(QMouseEvent *e)
+{
+ Q3ListBoxItem * i = itemAt(e->pos());
+ if (i != d->highlighted) {
+ if (i) {
+ emit onItem(i);
+ } else {
+ emit onViewport();
+ }
+ d->highlighted = i;
+ }
+
+ if (d->rubber) {
+ QRect r = d->rubber->normalized();
+ drawRubber();
+ d->rubber->setCoords(d->rubber->x(), d->rubber->y(), e->x(), e->y());
+ doRubberSelection(r, d->rubber->normalized());
+ drawRubber();
+ return;
+ }
+
+ if (((e->state() & (Qt::RightButton | Qt::LeftButton | Qt::MidButton)) == 0) ||
+ d->ignoreMoves)
+ return;
+
+ // hack to keep the combo (and what else?) working: if we get a
+ // move outside the listbox without having seen a press, discard
+ // it.
+ if (!QRect(0, 0, visibleWidth(), visibleHeight()).contains(e->pos()) &&
+ ((d->mousePressColumn < 0 && d->mousePressRow < 0)
+ || (e->state() == Qt::NoButton && !d->pressedItem)))
+ return;
+
+ // figure out in what direction to drag-select and perhaps scroll
+ int dx = 0;
+ int x = e->x();
+ if (x >= visibleWidth()) {
+ x = visibleWidth()-1;
+ dx = 1;
+ } else if (x < 0) {
+ x = 0;
+ dx = -1;
+ }
+ d->mouseMoveColumn = columnAt(x + contentsX());
+
+ // sanitize mousePressColumn, if we got here without a mouse press event
+ if (d->mousePressColumn < 0 && d->mouseMoveColumn >= 0)
+ d->mousePressColumn = d->mouseMoveColumn;
+ if (d->mousePressColumn < 0 && d->currentColumn >= 0)
+ d->mousePressColumn = d->currentColumn;
+
+ // if it's beyond the last column, use the last one
+ if (d->mouseMoveColumn < 0)
+ d->mouseMoveColumn = dx >= 0 ? numColumns()-1 : 0;
+
+ // repeat for y
+ int dy = 0;
+ int y = e->y();
+ if (y >= visibleHeight()) {
+ y = visibleHeight()-1;
+ dy = 1;
+ } else if (y < 0) {
+ y = 0;
+ dy = -1;
+ }
+ d->mouseMoveRow = rowAt(y + contentsY());
+
+ if (d->mousePressRow < 0 && d->mouseMoveRow >= 0)
+ d->mousePressRow = d->mouseMoveRow;
+ if (d->mousePressRow < 0 && d->currentRow >= 0)
+ d->mousePressRow = d->currentRow;
+
+ if (d->mousePressRow < 0)
+ d->mousePressRow = rowAt(x + contentsX());
+
+ d->scrollPos = QPoint(dx, dy);
+
+ if ((dx || dy) && !d->scrollTimer && e->state() == Qt::LeftButton && e->button() != Qt::LeftButton) {
+ // start autoscrolling if necessary
+ d->scrollTimer = new QTimer(this);
+ connect(d->scrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+ d->scrollTimer->start(100, false);
+ doAutoScroll();
+ } else if (!d->scrollTimer) {
+ // or just select the required bits
+ updateSelection();
+ }
+}
+
+
+
+void Q3ListBox::updateSelection()
+{
+ if (d->mouseMoveColumn >= 0 && d->mouseMoveRow >= 0 &&
+ d->mousePressColumn >= 0 && d->mousePressRow >= 0) {
+ Q3ListBoxItem * i = item(d->mouseMoveColumn * numRows() +
+ d->mouseMoveRow);
+#ifndef QT_NO_ACCESSIBILITY
+ int ind = index(i);
+#endif
+ if (selectionMode() == Single || selectionMode() == NoSelection) {
+ if (i && (d->mouseInternalPress || (windowType() == Qt::Popup)))
+ setCurrentItem(i);
+ } else {
+ if (d->selectionMode == Extended && (
+ (d->current == d->pressedItem && d->pressedSelected) ||
+ (d->dirtyDrag && !d->dragging))) {
+ if (d->dirtyDrag && !d->dragging) // emit after dragging stops
+ d->dirtyDrag = false;
+ else
+ clearSelection(); // don't reset drag-selected items
+ d->pressedItem = 0;
+ if (i && i->isSelectable()) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ i->s = true;
+ blockSignals(block);
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged);
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::SelectionAdd);
+#endif
+ }
+ triggerUpdate(false);
+ } else {
+ int c = qMin(d->mouseMoveColumn, d->mousePressColumn);
+ int r = qMin(d->mouseMoveRow, d->mousePressRow);
+ int c2 = qMax(d->mouseMoveColumn, d->mousePressColumn);
+ int r2 = qMax(d->mouseMoveRow, d->mousePressRow);
+ bool changed = false;
+ while(c <= c2) {
+ Q3ListBoxItem * i = item(c*numRows()+r);
+ int rtmp = r;
+ while(i && rtmp <= r2) {
+ if ((bool)i->s != (bool)d->select && i->isSelectable()) {
+ i->s = d->select;
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged);
+ QAccessible::updateAccessibility(viewport(), ind+1, d->select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove);
+#endif
+ i->dirty = true;
+ d->dirtyDrag = changed = true;
+ }
+ i = i->n;
+ rtmp++;
+ }
+ c++;
+ }
+ if (changed) {
+ if (!d->dragging) // emit after dragging stops instead
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+#endif
+ triggerUpdate(false);
+ }
+ }
+ if (i)
+ setCurrentItem(i);
+ }
+ }
+}
+
+void Q3ListBox::repaintSelection()
+{
+ if (d->numColumns == 1) {
+ for (uint i = topItem(); itemVisible(i) && i < count(); ++i) {
+ Q3ListBoxItem *it = item(i);
+ if (!it)
+ break;
+ if (it->isSelected())
+ updateItem(it);
+ }
+ } else {
+ for (uint i = 0; i < count(); ++i) {
+ Q3ListBoxItem *it = item(i);
+ if (!it)
+ break;
+ if (it->isSelected())
+ updateItem(it);
+ }
+ }
+}
+
+/*! \reimp
+*/
+
+void Q3ListBox::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+ if (!receivers(SIGNAL(contextMenuRequested(Q3ListBoxItem*,QPoint)))) {
+ e->ignore();
+ return;
+ }
+ if (e->reason() == QContextMenuEvent::Keyboard) {
+ Q3ListBoxItem *i = item(currentItem());
+ if (i) {
+ QRect r = itemRect(i);
+ emit contextMenuRequested(i, mapToGlobal(r.topLeft() + QPoint(width() / 2, r.height() / 2)));
+ }
+ } else {
+ Q3ListBoxItem * i = itemAt(contentsToViewport(e->pos()));
+ emit contextMenuRequested(i, e->globalPos());
+ }
+}
+
+/*!\reimp
+*/
+void Q3ListBox::keyPressEvent(QKeyEvent *e)
+{
+ if ((e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab)
+ && e->state() & Qt::ControlButton)
+ e->ignore();
+
+ if (count() == 0) {
+ e->ignore();
+ return;
+ }
+
+ QPointer<Q3ListBox> selfCheck = this;
+
+ Q3ListBoxItem *old = d->current;
+ if (!old) {
+ setCurrentItem(d->head);
+ if (d->selectionMode == Single)
+ setSelected(d->head, true);
+ e->ignore();
+ return;
+ }
+
+ bool selectCurrent = false;
+ switch (e->key()) {
+ case Qt::Key_Up:
+ {
+ d->currInputString.clear();
+ if (currentItem() > 0) {
+ setCurrentItem(currentItem() - 1);
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ }
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Down:
+ {
+ d->currInputString.clear();
+ if (currentItem() < (int)count() - 1) {
+ setCurrentItem(currentItem() + 1);
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ }
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Left:
+ {
+ d->currInputString.clear();
+ if (currentColumn() > 0) {
+ setCurrentItem(currentItem() - numRows());
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ } else if (numColumns() > 1 && currentItem() > 0) {
+ int row = currentRow();
+ setCurrentItem(currentRow() - 1 + (numColumns() - 1) * numRows());
+
+ if (currentItem() == -1)
+ setCurrentItem(row - 1 + (numColumns() - 2) * numRows());
+
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ } else {
+ QApplication::sendEvent(horizontalScrollBar(), e);
+ }
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Right:
+ {
+ d->currInputString.clear();
+ if (currentColumn() < numColumns()-1) {
+ int row = currentRow();
+ int i = currentItem();
+ Q3ListBoxItem *it = item(i + numRows());
+ if (!it)
+ it = item(count()-1);
+ setCurrentItem(it);
+
+ if (currentItem() == -1) {
+ if (row < numRows() - 1)
+ setCurrentItem(row + 1);
+ else
+ setCurrentItem(i);
+ }
+
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ } else if (numColumns() > 1 && currentRow() < numRows()) {
+ if (currentRow() + 1 < numRows()) {
+ setCurrentItem(currentRow() + 1);
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ }
+ } else {
+ QApplication::sendEvent(horizontalScrollBar(), e);
+ }
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Next:
+ {
+ d->currInputString.clear();
+ int i = 0;
+ if (numColumns() == 1) {
+ i = currentItem() + numItemsVisible();
+ i = i > (int)count() - 1 ? (int)count() - 1 : i;
+ setCurrentItem(i);
+ setBottomItem(i);
+ } else {
+ // I'm not sure about this behavior...
+ if (currentRow() == numRows() - 1)
+ i = currentItem() + numRows();
+ else
+ i = currentItem() + numRows() - currentRow() - 1;
+ i = i > (int)count() - 1 ? (int)count() - 1 : i;
+ setCurrentItem(i);
+ }
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Prior:
+ {
+ selectCurrent = true;
+ d->currInputString.clear();
+ int i;
+ if (numColumns() == 1) {
+ i = currentItem() - numItemsVisible();
+ i = i < 0 ? 0 : i;
+ setCurrentItem(i);
+ setTopItem(i);
+ } else {
+ // I'm not sure about this behavior...
+ if (currentRow() == 0)
+ i = currentItem() - numRows();
+ else
+ i = currentItem() - currentRow();
+ i = i < 0 ? 0 : i;
+ setCurrentItem(i);
+ }
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Space:
+ {
+ selectCurrent = true;
+ d->currInputString.clear();
+ toggleCurrentItem();
+ if (selectionMode() == Extended && d->current->isSelected())
+ emit highlighted(currentItem());
+ if (selfCheck && (!(e->state() & Qt::ShiftButton) || !d->selectAnchor))
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ {
+ selectCurrent = true;
+ d->currInputString.clear();
+ if (currentItem() >= 0 && selectionMode() != NoSelection) {
+ QString tmp = item(currentItem())->text();
+ emit selected(currentItem());
+ emit selected(item(currentItem()));
+ if (!tmp.isEmpty())
+ emit selected(tmp);
+ emit returnPressed(item(currentItem()));
+ }
+ if (selfCheck && (!(e->state() & Qt::ShiftButton) || !d->selectAnchor))
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_Home:
+ {
+ selectCurrent = true;
+ d->currInputString.clear();
+ setCurrentItem(0);
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ case Qt::Key_End:
+ {
+ selectCurrent = true;
+ d->currInputString.clear();
+ int i = (int)count() - 1;
+ setCurrentItem(i);
+ handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton);
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = d->current;
+ }
+ break;
+ default:
+ {
+ if (!e->text().isEmpty() && e->text()[0].isPrint() && count()) {
+ int curItem = currentItem();
+ if (curItem == -1)
+ curItem = 0;
+ if (!d->inputTimer->isActive()) {
+ d->currInputString = e->text();
+ curItem = d->findItemByName(++curItem, d->currInputString);
+ } else {
+ d->inputTimer->stop();
+ d->currInputString += e->text();
+ int oldCurItem = curItem;
+ curItem = d->findItemByName(curItem, d->currInputString);
+ if (curItem < 0) {
+ curItem = d->findItemByName(++oldCurItem, e->text());
+ d->currInputString = e->text();
+ }
+ }
+ if (curItem >= 0)
+ setCurrentItem(curItem);
+ if (curItem >= 0 && selectionMode() == Q3ListBox::Extended) {
+ bool changed = false;
+ bool block = signalsBlocked();
+ blockSignals(true);
+ selectAll(false);
+ blockSignals(block);
+ Q3ListBoxItem *i = item(curItem);
+ if (!i->s && i->isSelectable()) {
+ changed = true;
+ i->s = true;
+ updateItem(i);
+ }
+ if (changed)
+ emit selectionChanged();
+ }
+ d->inputTimer->start(400, true);
+ } else {
+ d->currInputString.clear();
+ if (e->state() & Qt::ControlButton) {
+ switch (e->key()) {
+ case Qt::Key_A:
+ selectAll(true);
+ break;
+ }
+ } else {
+ e->ignore();
+ }
+ }
+ }
+ }
+
+ if (selfCheck && selectCurrent && selectionMode() == Single &&
+ d->current && !d->current->s) {
+ updateItem(d->current);
+ setSelected(d->current, true);
+ }
+}
+
+
+/*!\reimp
+*/
+void Q3ListBox::focusInEvent(QFocusEvent *e)
+{
+ d->mousePressRow = -1;
+ d->mousePressColumn = -1;
+ d->inMenuMode = false;
+ if (e->reason() != Qt::MouseFocusReason && !d->current && d->head) {
+ d->current = d->head;
+ Q3ListBoxItem *i = d->current;
+ QString tmp;
+ if (i)
+ tmp = i->text();
+ int tmp2 = index(i);
+ emit highlighted(i);
+ if (!tmp.isNull())
+ emit highlighted(tmp);
+ emit highlighted(tmp2);
+ emit currentChanged(i);
+ }
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this))
+ repaintSelection();
+
+ if (d->current)
+ updateItem(currentItem());
+}
+
+
+/*!\reimp
+*/
+void Q3ListBox::focusOutEvent(QFocusEvent *e)
+{
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) {
+ d->inMenuMode =
+ e->reason() == Qt::PopupFocusReason ||
+ (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar"));
+ if (!d->inMenuMode)
+ repaintSelection();
+ }
+
+ if (d->current)
+ updateItem(currentItem());
+}
+
+/*!\reimp
+*/
+bool Q3ListBox::eventFilter(QObject *o, QEvent *e)
+{
+ return Q3ScrollView::eventFilter(o, e);
+}
+
+/*!
+ Repaints the item at position \a index in the list.
+*/
+
+void Q3ListBox::updateItem(int index)
+{
+ if (index >= 0)
+ updateItem(item(index));
+}
+
+
+/*!
+ \overload
+
+ Repaints the Q3ListBoxItem \a i.
+*/
+
+void Q3ListBox::updateItem(Q3ListBoxItem * i)
+{
+ if (!i)
+ return;
+ i->dirty = true;
+ d->updateTimer->start(0, true);
+}
+
+
+/*!
+ \property Q3ListBox::selectionMode
+ \brief the selection mode of the list box
+
+ Sets the list box's selection mode, which may be one of \c Single
+ (the default), \c Extended, \c Multi or \c NoSelection.
+
+ \sa SelectionMode
+*/
+
+void Q3ListBox::setSelectionMode(SelectionMode mode)
+{
+ if (d->selectionMode == mode)
+ return;
+
+ if ((selectionMode() == Multi || selectionMode() == Extended)
+ && (mode == Q3ListBox::Single || mode == Q3ListBox::NoSelection)){
+ clearSelection();
+ if ((mode == Q3ListBox::Single) && currentItem())
+ setSelected(currentItem(), true);
+ }
+
+ d->selectionMode = mode;
+ triggerUpdate(true);
+}
+
+
+Q3ListBox::SelectionMode Q3ListBox::selectionMode() const
+{
+ return d->selectionMode;
+}
+
+
+/*!
+ \property Q3ListBox::multiSelection
+ \brief whether or not the list box is in Multi selection mode
+
+ Consider using the \l Q3ListBox::selectionMode property instead of
+ this property.
+
+ When setting this property, Multi selection mode is used if set to true and
+ to Single selection mode if set to false.
+
+ When getting this property, true is returned if the list box is in
+ Multi selection mode or Extended selection mode, and false if it is
+ in Single selection mode or NoSelection mode.
+
+ \sa selectionMode
+*/
+
+bool Q3ListBox::isMultiSelection() const
+{
+ return selectionMode() == Multi || selectionMode() == Extended;
+}
+
+void Q3ListBox::setMultiSelection(bool enable)
+{
+ setSelectionMode(enable ? Multi : Single);
+}
+
+
+/*!
+ Toggles the selection status of currentItem() and repaints if the
+ list box is a \c Multi selection list box.
+
+ \sa setMultiSelection()
+*/
+
+void Q3ListBox::toggleCurrentItem()
+{
+ if (selectionMode() == Single ||
+ selectionMode() == NoSelection ||
+ !d->current)
+ return;
+
+ if (d->current->s || d->current->isSelectable()) {
+ d->current->s = !d->current->s;
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ int ind = index(d->current);
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged);
+ QAccessible::updateAccessibility(viewport(), ind+1, d->current->s ? QAccessible::SelectionAdd : QAccessible::SelectionRemove);
+#endif
+ }
+ updateItem(d->current);
+}
+
+
+/*!
+ \overload
+
+ If \a select is true the item at position \a index is selected;
+ otherwise the item is deselected.
+*/
+
+void Q3ListBox::setSelected(int index, bool select)
+{
+ setSelected(item(index), select);
+}
+
+
+/*!
+ Selects \a item if \a select is true or unselects it if \a select
+ is false, and repaints the item appropriately.
+
+ If the list box is a \c Single selection list box and \a select is
+ true, setSelected() calls setCurrentItem().
+
+ If the list box is a \c Single selection list box, \a select is
+ false, setSelected() calls clearSelection().
+
+ \sa setMultiSelection(), setCurrentItem(), clearSelection(), currentItem()
+*/
+
+void Q3ListBox::setSelected(Q3ListBoxItem * item, bool select)
+{
+ if (!item || !item->isSelectable() ||
+ (bool)item->s == select || d->selectionMode == NoSelection)
+ return;
+
+ int ind = index(item);
+ bool emitHighlighted = (d->current != item) || ( select && (item->s != (uint) select) );
+ if (selectionMode() == Single) {
+ if (d->current != item) {
+ Q3ListBoxItem *o = d->current;
+ if (d->current && d->current->s)
+ d->current->s = false;
+ d->current = item;
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::Focus);
+#endif
+ d->currentColumn = ind / numRows();
+ d->currentRow = ind % numRows();
+
+ if (o)
+ updateItem(o);
+ }
+ }
+
+ item->s = (uint)select;
+ updateItem(item);
+
+ if (d->selectionMode == Single && select) {
+ emit selectionChanged(item);
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged);
+#endif
+ }
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+ if (d->selectionMode != Single)
+ QAccessible::updateAccessibility(viewport(), ind+1, select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove);
+#endif
+
+ if (emitHighlighted) {
+ QString tmp;
+ if (item)
+ tmp = item->text();
+ int tmp2 = index(item);
+ emit highlighted(item);
+ if (!tmp.isNull())
+ emit highlighted(tmp);
+ emit highlighted(tmp2);
+ emit currentChanged(item);
+ }
+}
+
+
+/*!
+ Returns true if item \a i is selected; otherwise returns false.
+*/
+
+bool Q3ListBox::isSelected(int i) const
+{
+ if (selectionMode() == Single && i != currentItem())
+ return false;
+
+ Q3ListBoxItem * lbi = item(i);
+ if (!lbi)
+ return false; // should not happen
+ return lbi->s;
+}
+
+
+/*!
+ \overload
+
+ Returns true if item \a i is selected; otherwise returns false.
+*/
+
+bool Q3ListBox::isSelected(const Q3ListBoxItem * i) const
+{
+ if (!i)
+ return false;
+
+ return i->s;
+}
+
+/*! Returns the selected item if the list box is in
+single-selection mode and an item is selected.
+
+If no items are selected or the list box is in another selection mode
+this function returns 0.
+
+\sa setSelected() setMultiSelection()
+*/
+
+Q3ListBoxItem* Q3ListBox::selectedItem() const
+{
+ if (d->selectionMode != Single)
+ return 0;
+ if (isSelected(currentItem()))
+ return d->current;
+ return 0;
+}
+
+
+/*!
+ Deselects all items, if possible.
+
+ Note that a \c Single selection list box will automatically select
+ an item if it has keyboard focus.
+*/
+
+void Q3ListBox::clearSelection()
+{
+ selectAll(false);
+}
+
+/*!
+ In \c Multi and \c Extended modes, this function sets all items to
+ be selected if \a select is true, and to be unselected if \a
+ select is false.
+
+ In \c Single and \c NoSelection modes, this function only changes
+ the selection status of currentItem().
+*/
+
+void Q3ListBox::selectAll(bool select)
+{
+ if (selectionMode() == Multi || selectionMode() == Extended) {
+ bool b = signalsBlocked();
+ blockSignals(true);
+ for (int i = 0; i < (int)count(); i++)
+ setSelected(i, select);
+ blockSignals(b);
+ emit selectionChanged();
+ } else if (d->current) {
+ Q3ListBoxItem * i = d->current;
+ setSelected(i, select);
+ }
+}
+
+/*!
+ Inverts the selection. Only works in \c Multi and \c Extended
+ selection mode.
+*/
+
+void Q3ListBox::invertSelection()
+{
+ if (d->selectionMode == Single ||
+ d->selectionMode == NoSelection)
+ return;
+
+ bool b = signalsBlocked();
+ blockSignals(true);
+ for (int i = 0; i < (int)count(); i++)
+ setSelected(i, !item(i)->isSelected());
+ blockSignals(b);
+ emit selectionChanged();
+}
+
+
+/*!
+ Not used anymore; provided for compatibility.
+*/
+
+void Q3ListBox::emitChangedSignal(bool)
+{
+}
+
+
+/*! \reimp */
+
+QSize Q3ListBox::sizeHint() const
+{
+ if (cachedSizeHint().isValid())
+ return cachedSizeHint();
+
+ ensurePolished();
+ doLayout();
+
+ int i=0;
+ while(i < 10 &&
+ i < (int)d->columnPos.size()-1 &&
+ d->columnPos[i] < 200)
+ i++;
+ int x;
+ x = qMin(200, d->columnPos[i] +
+ 2 * style()->pixelMetric(QStyle::PM_DefaultFrameWidth));
+ x = qMax(40, x);
+
+ i = 0;
+ while(i < 10 &&
+ i < (int)d->rowPos.size()-1 &&
+ d->rowPos[i] < 200)
+ i++;
+ int y;
+ y = qMin(200, d->rowPos[i] +
+ 2 * style()->pixelMetric(QStyle::PM_DefaultFrameWidth));
+ y = qMax(40, y);
+
+ QSize s(x, y);
+ setCachedSizeHint(s);
+ return s;
+}
+
+/*!
+ \reimp
+*/
+
+QSize Q3ListBox::minimumSizeHint() const
+{
+ return Q3ScrollView::minimumSizeHint();
+}
+
+
+/*!
+ Ensures that a single paint event will occur at the end of the
+ current event loop iteration. If \a doLayout is true, the layout
+ is also redone.
+*/
+
+void Q3ListBox::triggerUpdate(bool doLayout)
+{
+ if (doLayout)
+ d->layoutDirty = d->mustPaintAll = true;
+ d->updateTimer->start(0, true);
+}
+
+
+void Q3ListBox::setColumnMode(LayoutMode mode)
+{
+ if (mode == Variable)
+ return;
+ d->rowModeWins = false;
+ d->columnMode = mode;
+ triggerUpdate(true);
+}
+
+
+void Q3ListBox::setColumnMode(int columns)
+{
+ if (columns < 1)
+ columns = 1;
+ d->columnMode = FixedNumber;
+ d->numColumns = columns;
+ d->rowModeWins = false;
+ triggerUpdate(true);
+}
+
+void Q3ListBox::setRowMode(LayoutMode mode)
+{
+ if (mode == Variable)
+ return;
+ d->rowModeWins = true;
+ d->rowMode = mode;
+ triggerUpdate(true);
+}
+
+
+void Q3ListBox::setRowMode(int rows)
+{
+ if (rows < 1)
+ rows = 1;
+ d->rowMode = FixedNumber;
+ d->numRows = rows;
+ d->rowModeWins = true;
+ triggerUpdate(true);
+}
+
+/*!
+ \property Q3ListBox::columnMode
+ \brief the column layout mode for this list box.
+
+ setColumnMode() sets the layout mode and adjusts the number of
+ displayed columns. The row layout mode automatically becomes \c
+ Variable, unless the column mode is \c Variable.
+
+ \sa setRowMode() rowMode numColumns
+*/
+
+
+Q3ListBox::LayoutMode Q3ListBox::columnMode() const
+{
+ if (d->rowModeWins)
+ return Variable;
+ else
+ return d->columnMode;
+}
+
+
+/*!
+ \property Q3ListBox::rowMode
+ \brief the row layout mode for this list box
+
+ This property is normally \c Variable.
+
+ setRowMode() sets the layout mode and adjusts the number of
+ displayed rows. The column layout mode automatically becomes \c
+ Variable, unless the row mode is \c Variable.
+
+ \sa columnMode
+*/
+
+
+Q3ListBox::LayoutMode Q3ListBox::rowMode() const
+{
+ if (d->rowModeWins)
+ return d->rowMode;
+ else
+ return Variable;
+}
+
+
+/*!
+ \property Q3ListBox::numColumns
+ \brief the number of columns in the list box
+
+ This is normally 1, but can be different if \l
+ Q3ListBox::columnMode or \l Q3ListBox::rowMode has been set.
+
+ \sa columnMode rowMode numRows
+*/
+
+int Q3ListBox::numColumns() const
+{
+ if (count() == 0)
+ return 0;
+ if (!d->rowModeWins && d->columnMode == FixedNumber)
+ return d->numColumns;
+ doLayout();
+ return d->columnPos.size()-1;
+}
+
+
+/*!
+ \property Q3ListBox::numRows
+ \brief the number of rows in the list box.
+
+ This is equal to the number of items in the default single-column
+ layout, but can be different.
+
+ \sa columnMode rowMode numColumns
+*/
+
+int Q3ListBox::numRows() const
+{
+ if (count() == 0)
+ return 0;
+ if (d->rowModeWins && d->rowMode == FixedNumber)
+ return d->numRows;
+ doLayout();
+ return d->rowPos.size()-1;
+}
+
+
+/*!
+ This function does the hard layout work. You should never need to
+ call it.
+*/
+
+void Q3ListBox::doLayout() const
+{
+ if (!d->layoutDirty || d->resizeTimer->isActive())
+ return;
+ ensurePolished();
+ int c = count();
+ switch(rowMode()) {
+ case FixedNumber:
+ // columnMode() is known to be Variable
+ tryGeometry(d->numRows, (c+d->numRows-1)/d->numRows);
+ break;
+ case FitToHeight:
+ // columnMode() is known to be Variable
+ if (d->head) {
+ // this is basically the FitToWidth code, but edited to use rows.
+ int maxh = 0;
+ Q3ListBoxItem * i = d->head;
+ while (i) {
+ int h = i->height(this);
+ if (maxh < h)
+ maxh = h;
+ i = i->n;
+ }
+ int vh = viewportSize(1, 1).height();
+ do {
+ int rows = vh / maxh;
+ if (rows > c)
+ rows = c;
+ if (rows < 1)
+ rows = 1;
+ if (variableHeight() && rows < c) {
+ do {
+ ++rows;
+ tryGeometry(rows, (c+rows-1)/rows);
+ } while (rows <= c &&
+ d->rowPos[(int)d->rowPos.size()-1] <= vh);
+ --rows;
+ }
+ tryGeometry(rows, (c+rows-1)/rows);
+ int nvh = viewportSize(d->columnPos[(int)d->columnPos.size()-1],
+ d->rowPos[(int)d->rowPos.size()-1]).height();
+ if (nvh < vh)
+ vh = nvh;
+ } while (d->rowPos.size() > 2 &&
+ vh < d->rowPos[(int)d->rowPos.size()-1]);
+ } else {
+ tryGeometry(1, 1);
+ }
+ break;
+ case Variable:
+ if (columnMode() == FixedNumber) {
+ tryGeometry((count()+d->numColumns-1)/d->numColumns,
+ d->numColumns);
+ } else if (d->head) { // FitToWidth, at least one item
+ int maxw = 0;
+ Q3ListBoxItem * i = d->head;
+ while (i) {
+ int w = i->width(this);
+ if (maxw < w)
+ maxw = w;
+ i = i->n;
+ }
+ int vw = viewportSize(1, 1).width();
+ do {
+ int cols = vw / maxw;
+ if (cols > c)
+ cols = c;
+ if (cols < 1)
+ cols = 1;
+ if (variableWidth() && cols < c) {
+ do {
+ ++cols;
+ tryGeometry((c+cols-1)/cols, cols);
+ } while (cols <= c &&
+ d->columnPos[(int)d->columnPos.size()-1] <= vw);
+ --cols;
+ }
+ tryGeometry((c+cols-1)/cols, cols);
+ int nvw = viewportSize(d->columnPos[(int)d->columnPos.size()-1],
+ d->rowPos[(int)d->rowPos.size()-1]).width();
+ if (nvw < vw)
+ vw = nvw;
+ } while (d->columnPos.size() > 2 &&
+ vw < d->columnPos[(int)d->columnPos.size()-1]);
+ } else {
+ tryGeometry(1, 1);
+ }
+ break;
+ }
+
+ d->layoutDirty = false;
+ int w = d->columnPos[(int)d->columnPos.size()-1];
+ int h = d->rowPos[(int)d->rowPos.size()-1];
+ QSize s(viewportSize(w, h));
+ w = qMax(w, s.width());
+
+ d->columnPosOne = d->columnPos[1];
+ // extend the column for simple single-column listboxes
+ if (columnMode() == FixedNumber && d->numColumns == 1 &&
+ d->columnPos[1] < w)
+ d->columnPos[1] = w;
+ ((Q3ListBox *)this)->resizeContents(w, h);
+}
+
+
+/*!
+ Lay the items out in a \a columns by \a rows array. The array may
+ be too big: doLayout() is expected to call this with the right
+ values.
+*/
+
+void Q3ListBox::tryGeometry(int rows, int columns) const
+{
+ if (columns < 1)
+ columns = 1;
+ d->columnPos.resize(columns+1);
+
+ if (rows < 1)
+ rows = 1;
+ d->rowPos.resize(rows+1);
+
+ // funky hack I: dump the height/width of each column/row in
+ // {column,row}Pos for later conversion to positions.
+ int c;
+ for(c=0; c<=columns; c++)
+ d->columnPos[c] = 0;
+ int r;
+ for(r=0; r<=rows; r++)
+ d->rowPos[r] = 0;
+ r = c = 0;
+ Q3ListBoxItem * i = d->head;
+ while (i && c < columns) {
+ if (i == d->current) {
+ d->currentRow = r;
+ d->currentColumn = c;
+ }
+
+ int w = i->width(this);
+ if (d->columnPos[c] < w)
+ d->columnPos[c] = w;
+ int h = i->height(this);
+ if (d->rowPos[r] < h)
+ d->rowPos[r] = h;
+ i = i->n;
+ r++;
+ if (r == rows) {
+ r = 0;
+ c++;
+ }
+ }
+ // funky hack II: if not variable {width,height}, unvariablify it.
+ if (!variableWidth()) {
+ int w = 0;
+ for(c=0; c<columns; c++)
+ if (w < d->columnPos[c])
+ w = d->columnPos[c];
+ for(c=0; c<columns; c++)
+ d->columnPos[c] = w;
+ }
+ if (!variableHeight()) {
+ int h = 0;
+ for(r=0; r<rows; r++)
+ if (h < d->rowPos[r])
+ h = d->rowPos[r];
+ for(r=0; r<rows; r++)
+ d->rowPos[r] = h;
+ }
+ // repair the hacking.
+ int x = 0;
+ for(c=0; c<=columns; c++) {
+ int w = d->columnPos[c];
+ d->columnPos[c] = x;
+ x += w;
+ }
+ int y = 0;
+ for(r=0; r<=rows; r++) {
+ int h = d->rowPos[r];
+ d->rowPos[r] = y;
+ y += h;
+ }
+}
+
+
+/*!
+ Returns the row index of the current item, or -1 if no item is the
+ current item.
+*/
+
+int Q3ListBox::currentRow() const
+{
+ if (!d->current)
+ return -1;
+ if (d->currentRow < 0)
+ d->layoutDirty = true;
+ if (d->layoutDirty)
+ doLayout();
+ return d->currentRow;
+}
+
+
+/*!
+ Returns the column index of the current item, or -1 if no item is
+ the current item.
+*/
+
+int Q3ListBox::currentColumn() const
+{
+ if (!d->current)
+ return -1;
+ if (d->currentColumn < 0)
+ d->layoutDirty = true;
+ if (d->layoutDirty)
+ doLayout();
+ return d->currentColumn;
+}
+
+
+void Q3ListBox::setTopItem(int index)
+{
+ if (index >= (int)count() || count() == 0)
+ return;
+ int col = index / numRows();
+ int y = d->rowPos[index-col*numRows()];
+ if (d->columnPos[col] >= contentsX() &&
+ d->columnPos[col+1] <= contentsX() + visibleWidth())
+ setContentsPos(contentsX(), y);
+ else
+ setContentsPos(d->columnPos[col], y);
+}
+
+/*!
+ Scrolls the list box so the item at position \a index in the list
+ is displayed in the bottom row of the list box.
+
+ \sa setTopItem()
+*/
+
+void Q3ListBox::setBottomItem(int index)
+{
+ if (index >= (int)count() || count() == 0)
+ return;
+ int col = index / numRows();
+ int y = d->rowPos[1+index-col*numRows()] - visibleHeight();
+ if (y < 0)
+ y = 0;
+ if (d->columnPos[col] >= contentsX() &&
+ d->columnPos[col+1] <= contentsX() + visibleWidth())
+ setContentsPos(contentsX(), y);
+ else
+ setContentsPos(d->columnPos[col], y);
+}
+
+
+/*!
+ Returns the item at point \a p, specified in viewport coordinates,
+ or a 0 if there is no item at \a p.
+
+ Use contentsToViewport() to convert between widget coordinates and
+ viewport coordinates.
+*/
+
+Q3ListBoxItem * Q3ListBox::itemAt(const QPoint& p) const
+{
+ if (d->layoutDirty)
+ doLayout();
+ QPoint np = p;
+
+ np -= viewport()->pos();
+ if (!viewport()->rect().contains(np))
+ return 0;
+
+ // take into account contents position
+ np = viewportToContents(np);
+
+ int x = np.x();
+ int y = np.y();
+
+ // return 0 when y is below the last row
+ if (y > d->rowPos[numRows()])
+ return 0;
+
+ int col = columnAt(x);
+ int row = rowAt(y);
+
+ Q3ListBoxItem *i = item(col * numRows() + row);
+ if (i && numColumns() > 1) {
+ if (d->columnPos[col] + i->width(this) >= x)
+ return i;
+ } else {
+ if (d->columnPos[col + 1] >= x)
+ return i;
+ }
+ return 0;
+}
+
+
+/*!
+ Ensures that the current item is visible.
+*/
+
+void Q3ListBox::ensureCurrentVisible()
+{
+ if (!d->current)
+ return;
+
+ doLayout();
+
+ int row = currentRow();
+ int column = currentColumn();
+ int w = (d->columnPos[column+1] - d->columnPos[column]) / 2;
+ int h = (d->rowPos[row+1] - d->rowPos[row]) / 2;
+ // next four lines are Bad. they mean that for pure left-to-right
+ // languages, textual list box items are displayed better than
+ // before when there is little space. for non-textual items, or
+ // other languages, it means... that you really should have enough
+ // space in the first place :)
+ if (numColumns() == 1)
+ w = 0;
+ if (w*2 > viewport()->width())
+ w = viewport()->width()/2;
+
+ ensureVisible(d->columnPos[column] + w, d->rowPos[row] + h, w, h);
+}
+
+
+/*! \internal */
+
+void Q3ListBox::doAutoScroll()
+{
+ if (d->scrollPos.x() < 0) {
+ // scroll left
+ int x = contentsX() - horizontalScrollBar()->singleStep();
+ if (x < 0)
+ x = 0;
+ if (x != contentsX()) {
+ d->mouseMoveColumn = columnAt(x);
+ updateSelection();
+ if (x < contentsX())
+ setContentsPos(x, contentsY());
+ }
+ } else if (d->scrollPos.x() > 0) {
+ // scroll right
+ int x = contentsX() + horizontalScrollBar()->singleStep();
+ if (x + visibleWidth() > contentsWidth())
+ x = contentsWidth() - visibleWidth();
+ if (x != contentsX()) {
+ d->mouseMoveColumn = columnAt(x + visibleWidth() - 1);
+ updateSelection();
+ if (x > contentsX())
+ setContentsPos(x, contentsY());
+ }
+ }
+
+ if (d->scrollPos.y() < 0) {
+ // scroll up
+ int y = contentsY() - verticalScrollBar()->singleStep();
+ if (y < 0)
+ y = 0;
+ if (y != contentsY()) {
+ y = contentsY() - verticalScrollBar()->singleStep();
+ d->mouseMoveRow = rowAt(y);
+ updateSelection();
+ }
+ } else if (d->scrollPos.y() > 0) {
+ // scroll down
+ int y = contentsY() + verticalScrollBar()->singleStep();
+ if (y + visibleHeight() > contentsHeight())
+ y = contentsHeight() - visibleHeight();
+ if (y != contentsY()) {
+ y = contentsY() + verticalScrollBar()->singleStep();
+ d->mouseMoveRow = rowAt(y + visibleHeight() - 1);
+ updateSelection();
+ }
+ }
+
+ if (d->scrollPos == QPoint(0, 0)) {
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ }
+}
+
+
+/*!
+ \property Q3ListBox::topItem
+ \brief the index of an item at the top of the screen.
+
+ When getting this property and the listbox has multiple columns,
+ an arbitrary item is selected and returned.
+
+ When setting this property, the list box is scrolled so the item
+ at position \e index in the list is displayed in the top row of
+ the list box.
+*/
+
+int Q3ListBox::topItem() const
+{
+ doLayout();
+
+ // move rightwards to the best column
+ int col = columnAt(contentsX());
+ int row = rowAt(contentsY());
+ return col * numRows() + row;
+}
+
+
+/*!
+ \property Q3ListBox::variableHeight
+ \brief whether this list box has variable-height rows
+
+ When the list box has variable-height rows (the default), each row
+ is as high as the highest item in that row. When it has same-sized
+ rows, all rows are as high as the highest item in the list box.
+
+ \sa variableWidth
+*/
+
+bool Q3ListBox::variableHeight() const
+{
+ return d->variableHeight;
+}
+
+
+void Q3ListBox::setVariableHeight(bool enable)
+{
+ if ((bool)d->variableHeight == enable)
+ return;
+
+ d->variableHeight = enable;
+ triggerUpdate(true);
+}
+
+
+/*!
+ \property Q3ListBox::variableWidth
+ \brief whether this list box has variable-width columns
+
+ When the list box has variable-width columns, each column is as
+ wide as the widest item in that column. When it has same-sized
+ columns (the default), all columns are as wide as the widest item
+ in the list box.
+
+ \sa variableHeight
+*/
+
+bool Q3ListBox::variableWidth() const
+{
+ return d->variableWidth;
+}
+
+
+void Q3ListBox::setVariableWidth(bool enable)
+{
+ if ((bool)d->variableWidth == enable)
+ return;
+
+ d->variableWidth = enable;
+ triggerUpdate(true);
+}
+
+
+/*!
+ Repaints only what really needs to be repainted.
+*/
+void Q3ListBox::refreshSlot()
+{
+ if (d->mustPaintAll ||
+ d->layoutDirty) {
+ d->mustPaintAll = false;
+ bool currentItemVisible = itemVisible(currentItem());
+ doLayout();
+ if (hasFocus() &&
+ currentItemVisible &&
+ d->currentColumn >= 0 &&
+ d->currentRow >= 0 &&
+ (d->columnPos[d->currentColumn] < contentsX() ||
+ d->columnPos[d->currentColumn+1]>contentsX()+visibleWidth() ||
+ d->rowPos[d->currentRow] < contentsY() ||
+ d->rowPos[d->currentRow+1] > contentsY()+visibleHeight()))
+ ensureCurrentVisible();
+ viewport()->repaint();
+ return;
+ }
+
+ QRegion r;
+ int x = contentsX();
+ int y = contentsY();
+ int col = columnAt(x);
+ int row = rowAt(y);
+ int top = row;
+ while(col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x)
+ col++;
+ while(top < (int)d->rowPos.size()-1 && d->rowPos[top+1] < y)
+ top++;
+ Q3ListBoxItem * i = item(col * numRows() + row);
+
+ while (i && (int)col < numColumns() &&
+ d->columnPos[col] < x + visibleWidth() ) {
+ int cw = d->columnPos[col+1] - d->columnPos[col];
+ while (i && row < numRows() && d->rowPos[row] <
+ y + visibleHeight()) {
+ if (i->dirty)
+ r = r.united(QRect(d->columnPos[col] - x, d->rowPos[row] - y,
+ cw, d->rowPos[row+1] - d->rowPos[row]));
+ row++;
+ i = i->n;
+ }
+ col++;
+ if (numColumns() > 1) {
+ row = top;
+ i = item(col * numRows() + row);
+ }
+ }
+
+ if (r.isEmpty())
+ viewport()->repaint();
+ else
+ viewport()->repaint(r);
+}
+
+
+/*! \reimp */
+
+void Q3ListBox::viewportPaintEvent(QPaintEvent * e)
+{
+ doLayout();
+ QWidget* vp = viewport();
+ QPainter p(vp);
+ QRegion r = e->region();
+
+#if 0
+ {
+ // this stuff has been useful enough times that from now I'm
+ // leaving it in the source.
+ uint i = 0;
+ qDebug("%s/%s: %i rects", className(), name(), r.rects().size());
+ while(i < r.rects().size()) {
+ qDebug("rect %d: %d, %d, %d, %d", i,
+ r.rects()[i].left(), r.rects()[i].top(),
+ r.rects()[i].width(), r.rects()[i].height());
+ i++;
+ }
+ qDebug("");
+ }
+#endif
+
+ int x = contentsX();
+ int y = contentsY();
+ int w = vp->width();
+ int h = vp->height();
+
+ int col = columnAt(x);
+ int top = rowAt(y);
+ int row = top;
+
+ Q3ListBoxItem * i = item(col*numRows() + row);
+
+ const QPalette &pal = palette();
+ p.setPen(pal.text().color());
+ p.setBackground(palette().brush(backgroundRole()).color());
+ while (i && (int)col < numColumns() && d->columnPos[col] < x + w) {
+ int cw = d->columnPos[col+1] - d->columnPos[col];
+ while (i && (int)row < numRows() && d->rowPos[row] < y + h) {
+ int ch = d->rowPos[row+1] - d->rowPos[row];
+ QRect itemRect(d->columnPos[col]-x, d->rowPos[row]-y, cw, ch);
+ QRegion tempRegion(itemRect);
+ QRegion itemPaintRegion(tempRegion.intersected(r ));
+ if (!itemPaintRegion.isEmpty()) {
+ p.save();
+ p.setClipRegion(itemPaintRegion);
+ p.translate(d->columnPos[col]-x, d->rowPos[row]-y);
+ paintCell(&p, row, col);
+ p.restore();
+ r = r.subtracted(itemPaintRegion);
+ }
+ row++;
+ if (i->dirty) {
+ // reset dirty flag only if the entire item was painted
+ if (itemPaintRegion == QRegion(itemRect))
+ i->dirty = false;
+ }
+ i = i->n;
+ }
+ col++;
+ if (numColumns() > 1) {
+ row = top;
+ i = item(col * numRows() + row);
+ }
+ }
+
+ if (r.isEmpty())
+ return;
+ p.setClipRegion(r);
+ p.fillRect(0, 0, w, h, viewport()->palette().brush(viewport()->backgroundRole()));
+
+ if(d->rubber && d->rubber->width() && d->rubber->height()) {
+ p.save();
+ p.setClipping(false);
+ // p.setRasterOp(NotROP); // ### fix - use qrubberband instead
+ QStyleOptionRubberBand opt;
+ opt.rect = d->rubber->normalized();
+ opt.palette = palette();
+ opt.shape = QRubberBand::Rectangle;
+ opt.opaque = false;
+ style()->drawControl(QStyle::CE_RubberBand, &opt, &p, this);
+ p.restore();
+ }
+}
+
+
+/*!
+ Returns the height in pixels of the item with index \a index. \a
+ index defaults to 0.
+
+ If \a index is too large, this function returns 0.
+*/
+
+int Q3ListBox::itemHeight(int index) const
+{
+ if (index >= (int)count() || index < 0)
+ return 0;
+ int r = index % numRows();
+ return d->rowPos[r+1] - d->rowPos[r];
+}
+
+
+/*!
+ Returns the index of the column at \a x, which is in the listbox's
+ coordinates, not in on-screen coordinates.
+
+ If there is no column that spans \a x, columnAt() returns -1.
+*/
+
+int Q3ListBox::columnAt(int x) const
+{
+ if (x < 0)
+ return -1;
+ if (!d->columnPos.size())
+ return -1;
+ if (x >= d->columnPos[(int)d->columnPos.size()-1])
+ return numColumns() - 1;
+
+ int col = 0;
+ while(col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x)
+ col++;
+ return col;
+}
+
+
+/*!
+ Returns the index of the row at \a y, which is in the listbox's
+ coordinates, not in on-screen coordinates.
+
+ If there is no row that spans \a y, rowAt() returns -1.
+*/
+
+int Q3ListBox::rowAt(int y) const
+{
+ if (y < 0)
+ return -1;
+
+ // find the top item, use bsearch for speed
+ int l = 0;
+ int r = d->rowPos.size() - 2;
+ if (r < 0)
+ return -1;
+ if (l <= d->rowPosCache && d->rowPosCache <= r) {
+ if (d->rowPos[qMax(l, d->rowPosCache - 10)] <= y
+ && y <= d->rowPos[qMin(r, d->rowPosCache + 10)]) {
+ l = qMax(l, d->rowPosCache - 10);
+ r = qMin(r, d->rowPosCache + 10);
+ }
+ }
+ int i = ((l+r+1) / 2);
+ while (r - l) {
+ if (d->rowPos[i] > y)
+ r = i -1;
+ else
+ l = i;
+ i = ((l+r+1) / 2);
+ }
+ d->rowPosCache = i;
+ if (d->rowPos[i] <= y && y <= d->rowPos[i+1] )
+ return i;
+
+ return d->count - 1;
+}
+
+
+/*!
+ Returns the rectangle on the screen that \a item occupies in
+ viewport()'s coordinates, or an invalid rectangle if \a item is 0
+ or is not currently visible.
+*/
+
+QRect Q3ListBox::itemRect(Q3ListBoxItem *item) const
+{
+ if (d->resizeTimer->isActive())
+ return QRect(0, 0, -1, -1);
+ if (!item)
+ return QRect(0, 0, -1, -1);
+
+ int i = index(item);
+ if (i == -1)
+ return QRect(0, 0, -1, -1);
+
+ int col = i / numRows();
+ int row = i % numRows();
+
+ int x = d->columnPos[col] - contentsX();
+ int y = d->rowPos[row] - contentsY();
+
+ QRect r(x, y, d->columnPos[col + 1] - d->columnPos[col],
+ d->rowPos[row + 1] - d->rowPos[row]);
+ if (r.intersects(QRect(0, 0, visibleWidth(), visibleHeight())))
+ return r;
+ return QRect(0, 0, -1, -1);
+}
+
+
+/*!
+ Using this method is quite inefficient. We suggest to use insertItem()
+ for inserting and sort() afterwards.
+
+ Inserts \a lbi at its sorted position in the list box and returns the
+ position.
+
+ All items must be inserted with inSort() to maintain the sorting
+ order. inSort() treats any pixmap (or user-defined type) as
+ lexicographically less than any string.
+
+ \sa insertItem(), sort()
+*/
+int Q3ListBox::inSort(const Q3ListBoxItem * lbi)
+{
+ if (!lbi)
+ return -1;
+
+ Q3ListBoxItem * i = d->head;
+ int c = 0;
+
+ while(i && i->text() < lbi->text()) {
+ i = i->n;
+ c++;
+ }
+ insertItem(lbi, c);
+ return c;
+}
+
+/*!
+ \overload
+ Using this method is quite inefficient. We suggest to use insertItem()
+ for inserting and sort() afterwards.
+
+ Inserts a new item of \a text at its sorted position in the list box and
+ returns the position.
+
+ All items must be inserted with inSort() to maintain the sorting
+ order. inSort() treats any pixmap (or user-defined type) as
+ lexicographically less than any string.
+
+ \sa insertItem(), sort()
+*/
+int Q3ListBox::inSort(const QString& text)
+{
+ Q3ListBoxItem *lbi = new Q3ListBoxText(text);
+
+ Q3ListBoxItem * i = d->head;
+ int c = 0;
+
+ while(i && i->text() < lbi->text()) {
+ i = i->n;
+ c++;
+ }
+ insertItem(lbi, c);
+ return c;
+}
+
+
+/*! \reimp */
+
+void Q3ListBox::resizeEvent(QResizeEvent *e)
+{
+ d->layoutDirty = (d->layoutDirty ||
+ rowMode() == FitToHeight ||
+ columnMode() == FitToWidth);
+
+ if (!d->layoutDirty && columnMode() == FixedNumber &&
+ d->numColumns == 1) {
+ int w = d->columnPosOne;
+ QSize s(viewportSize(w, contentsHeight()));
+ w = qMax(w, s.width());
+ d->columnPos[1] = qMax(w, d->columnPosOne);
+ resizeContents(d->columnPos[1], contentsHeight());
+ }
+
+ if (d->resizeTimer->isActive())
+ d->resizeTimer->stop();
+ if (d->rowMode == FixedNumber && d->columnMode == FixedNumber) {
+ bool currentItemVisible = itemVisible(currentItem());
+ doLayout();
+ Q3ScrollView::resizeEvent(e);
+ if (currentItemVisible)
+ ensureCurrentVisible();
+ if (d->current)
+ viewport()->repaint(itemRect(d->current));
+ } else if ((d->columnMode == FitToWidth || d->rowMode == FitToHeight) && !(isVisible())) {
+ Q3ScrollView::resizeEvent(e);
+ } else if (d->layoutDirty) {
+ d->resizeTimer->start(100, true);
+ resizeContents(contentsWidth() - (e->oldSize().width() - e->size().width()),
+ contentsHeight() - (e->oldSize().height() - e->size().height()));
+ Q3ScrollView::resizeEvent(e);
+ } else {
+ Q3ScrollView::resizeEvent(e);
+ }
+}
+
+/*!
+ \internal
+*/
+
+void Q3ListBox::adjustItems()
+{
+ triggerUpdate(true);
+ ensureCurrentVisible();
+}
+
+
+/*!
+ Provided for compatibility with the old Q3ListBox. We recommend
+ using Q3ListBoxItem::paint() instead.
+
+ Repaints the cell at \a row, \a col using painter \a p.
+*/
+
+void Q3ListBox::paintCell(QPainter * p, int row, int col)
+{
+ bool drawActiveSelection = hasFocus() || d->inMenuMode ||
+ !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this);
+ QPalette pal = palette();
+ if(!drawActiveSelection)
+ pal.setCurrentColorGroup(QPalette::Inactive);
+
+ int cw = d->columnPos[col+1] - d->columnPos[col];
+ int ch = d->rowPos[row+1] - d->rowPos[row];
+ Q3ListBoxItem * i = item(col*numRows()+row);
+ p->save();
+ if (i->s) {
+ if (i->custom_highlight) {
+ p->fillRect(0, 0, cw, ch, pal.brush(viewport()->foregroundRole()));
+ p->setPen(pal.highlightedText().color());
+ p->setBackground(pal.highlight());
+ } else if (numColumns() == 1) {
+ p->fillRect(0, 0, cw, ch, pal.brush(QPalette::Highlight));
+ p->setPen(pal.highlightedText().color());
+ p->setBackground(pal.highlight());
+ } else {
+ int iw = i->width(this);
+ p->fillRect(0, 0, iw, ch, pal.brush(QPalette::Highlight));
+ p->fillRect(iw, 0, cw - iw + 1, ch, viewport()->palette().brush(viewport()->backgroundRole()));
+ p->setPen(pal.highlightedText().color());
+ p->setBackground(pal.highlight());
+ }
+ } else {
+ p->fillRect(0, 0, cw, ch, viewport()->palette().brush(viewport()->backgroundRole()));
+ }
+
+ i->paint(p);
+
+ if (d->current == i && hasFocus() && !i->custom_highlight) {
+ if (numColumns() > 1)
+ cw = i->width(this);
+ QStyleOptionFocusRect opt;
+ opt.rect.setRect(0, 0, cw, ch);
+ opt.palette = pal;
+ opt.state = QStyle::State_FocusAtBorder;
+ if (i->isSelected())
+ opt.backgroundColor = pal.highlight().color();
+ else
+ opt.backgroundColor = pal.base().color();
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this);
+ }
+
+ p->restore();
+}
+
+/*!
+ Returns the width of the widest item in the list box.
+*/
+
+long Q3ListBox::maxItemWidth() const
+{
+ if (d->layoutDirty)
+ doLayout();
+ long m = 0;
+ int i = d->columnPos.size();
+ while(i--)
+ if (m < d->columnPos[i])
+ m = d->columnPos[i];
+ return m;
+}
+
+
+/*! \reimp */
+
+void Q3ListBox::showEvent(QShowEvent *)
+{
+ d->ignoreMoves = false;
+ d->mousePressRow = -1;
+ d->mousePressColumn = -1;
+ d->mustPaintAll = false;
+ ensureCurrentVisible();
+}
+
+/*!
+ \fn bool Q3ListBoxItem::isSelected() const
+
+ Returns true if the item is selected; otherwise returns false.
+
+ \sa Q3ListBox::isSelected(), isCurrent()
+*/
+
+/*!
+ Returns true if the item is the current item; otherwise returns
+ false.
+
+ \sa Q3ListBox::currentItem(), Q3ListBox::item(), isSelected()
+*/
+bool Q3ListBoxItem::isCurrent() const
+{
+ return listBox() && listBox()->hasFocus() &&
+ listBox()->item(listBox()->currentItem()) == this;
+}
+
+/*!
+ \fn void Q3ListBox::centerCurrentItem()
+
+ If there is a current item, the list box is scrolled so that this
+ item is displayed centered.
+
+ \sa Q3ListBox::ensureCurrentVisible()
+*/
+
+/*!
+ Returns a pointer to the list box containing this item.
+*/
+
+Q3ListBox * Q3ListBoxItem::listBox() const
+{
+ return lbox;
+}
+
+
+/*!
+ Removes \a item from the list box and causes an update of the
+ screen display. The item is not deleted. You should normally not
+ need to call this function because Q3ListBoxItem::~Q3ListBoxItem()
+ calls it. The normal way to delete an item is with \c delete.
+
+ \sa Q3ListBox::insertItem()
+*/
+void Q3ListBox::takeItem(const Q3ListBoxItem * item)
+{
+ if (!item || d->clearing)
+ return;
+ d->cache = 0;
+ d->count--;
+ if (item == d->last)
+ d->last = d->last->p;
+ if (item->p && item->p->n == item)
+ item->p->n = item->n;
+ if (item->n && item->n->p == item)
+ item->n->p = item->p;
+ if (d->head == item) {
+ d->head = item->n;
+ d->currentColumn = d->currentRow = -1;
+ }
+
+ if (d->current == item) {
+ d->current = item->n ? item->n : item->p;
+ Q3ListBoxItem *i = d->current;
+ QString tmp;
+ if (i)
+ tmp = i->text();
+ int tmp2 = index(i);
+ emit highlighted(i);
+ if (!tmp.isNull())
+ emit highlighted(tmp);
+ emit highlighted(tmp2);
+ emit currentChanged(i);
+ }
+ if (d->tmpCurrent == item)
+ d->tmpCurrent = d->current;
+ if (d->selectAnchor == item)
+ d->selectAnchor = d->current;
+
+ if (item->s)
+ emit selectionChanged();
+ ((Q3ListBoxItem *)item)->lbox = 0;
+ triggerUpdate(true);
+}
+
+/*!
+ \internal
+ Finds the next item after start beginning with \a text.
+*/
+
+int Q3ListBoxPrivate::findItemByName(int start, const QString &text)
+{
+ if (start < 0 || (uint)start >= listBox->count())
+ start = 0;
+ QString match = text.toLower();
+ if (match.length() < 1)
+ return start;
+ QString curText;
+ int item = start;
+ do {
+ curText = listBox->text(item).toLower();
+ if (curText.startsWith(match))
+ return item;
+ item++;
+ if ((uint)item == listBox->count())
+ item = 0;
+ } while (item != start);
+ return -1;
+}
+
+/*!
+ \internal
+*/
+
+void Q3ListBox::clearInputString()
+{
+ d->currInputString.clear();
+}
+
+/*!
+ Finds the first list box item that has the text \a text and
+ returns it, or returns 0 of no such item could be found. If \c
+ ComparisonFlags are specified in \a compare then these flags
+ are used, otherwise the default is a case-insensitive, "begins
+ with" search.
+*/
+
+Q3ListBoxItem *Q3ListBox::findItem(const QString &text, ComparisonFlags compare) const
+{
+ if (text.isEmpty())
+ return 0;
+
+ if (compare == CaseSensitive || compare == 0)
+ compare |= ExactMatch;
+
+ QString itmtxt;
+ QString comtxt = text;
+ if (!(compare & CaseSensitive))
+ comtxt = text.toLower();
+
+ Q3ListBoxItem *item;
+ if (d->current)
+ item = d->current;
+ else
+ item = d->head;
+
+ Q3ListBoxItem *beginsWithItem = 0;
+ Q3ListBoxItem *endsWithItem = 0;
+ Q3ListBoxItem *containsItem = 0;
+
+ if (item) {
+ for (; item; item = item->n) {
+ if (!(compare & CaseSensitive))
+ itmtxt = item->text().toLower();
+ else
+ itmtxt = item->text();
+
+ if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt)
+ return item;
+ if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt))
+ beginsWithItem = containsItem = item;
+ if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt))
+ endsWithItem = containsItem = item;
+ if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt))
+ containsItem = item;
+ }
+
+ if (d->current && d->head) {
+ item = d->head;
+ for (; item && item != d->current; item = item->n) {
+ if (!(compare & CaseSensitive))
+ itmtxt = item->text().toLower();
+ else
+ itmtxt = item->text();
+
+ if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt)
+ return item;
+ if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt))
+ beginsWithItem = containsItem = item;
+ if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt))
+ endsWithItem = containsItem = item;
+ if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt))
+ containsItem = item;
+ }
+ }
+ }
+
+ // Obey the priorities
+ if (beginsWithItem)
+ return beginsWithItem;
+ else if (endsWithItem)
+ return endsWithItem;
+ else if (containsItem)
+ return containsItem;
+ return 0;
+}
+
+/*!
+ \internal
+*/
+
+void Q3ListBox::drawRubber()
+{
+ if (!d->rubber)
+ return;
+ if (!d->rubber->width() && !d->rubber->height())
+ return;
+ update();
+}
+
+/*!
+ \internal
+*/
+
+void Q3ListBox::doRubberSelection(const QRect &old, const QRect &rubber)
+{
+ Q3ListBoxItem *i = d->head;
+ QRect ir, pr;
+ bool changed = false;
+ for (; i; i = i->n) {
+ ir = itemRect(i);
+ if (ir == QRect(0, 0, -1, -1))
+ continue;
+ if (i->isSelected() && !ir.intersects(rubber) && ir.intersects(old)) {
+ i->s = false;
+ pr = pr.united(ir);
+ changed = true;
+ } else if (!i->isSelected() && ir.intersects(rubber)) {
+ if (i->isSelectable()) {
+ i->s = true;
+ pr = pr.united(ir);
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+#endif
+ }
+ viewport()->repaint(pr);
+}
+
+
+/*!
+ Returns true if the user is selecting items using a rubber band
+ rectangle; otherwise returns false.
+*/
+
+bool Q3ListBox::isRubberSelecting() const
+{
+ return d->rubber != 0;
+}
+
+
+/*!
+ Returns the item that comes after this in the list box. If this is
+ the last item, 0 is returned.
+
+ \sa prev()
+*/
+
+Q3ListBoxItem *Q3ListBoxItem::next() const
+{
+ return n;
+}
+
+/*!
+ Returns the item which comes before this in the list box. If this
+ is the first item, 0 is returned.
+
+ \sa next()
+*/
+
+Q3ListBoxItem *Q3ListBoxItem::prev() const
+{
+ return p;
+}
+
+/*!
+ Returns the first item in this list box. If the list box is empty,
+ returns 0.
+*/
+
+Q3ListBoxItem *Q3ListBox::firstItem() const
+{
+ return d->head;
+}
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int _cdecl cmpListBoxItems(const void *n1, const void *n2)
+#else
+static int cmpListBoxItems(const void *n1, const void *n2)
+#endif
+{
+ if (!n1 || !n2)
+ return 0;
+
+ Q3ListBoxPrivate::SortableItem *i1 = (Q3ListBoxPrivate::SortableItem *)n1;
+ Q3ListBoxPrivate::SortableItem *i2 = (Q3ListBoxPrivate::SortableItem *)n2;
+
+ return i1->item->text().localeAwareCompare(i2->item->text());
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+/*!
+ If \a ascending is true sorts the items in ascending order;
+ otherwise sorts in descending order.
+
+ To compare the items, the text (Q3ListBoxItem::text()) of the items
+ is used.
+*/
+
+void Q3ListBox::sort(bool ascending)
+{
+ if (count() == 0)
+ return;
+
+ d->cache = 0;
+
+ Q3ListBoxPrivate::SortableItem *items = new Q3ListBoxPrivate::SortableItem[count()];
+
+ Q3ListBoxItem *item = d->head;
+ int i = 0;
+ for (; item; item = item->n)
+ items[i++].item = item;
+
+ qsort(items, count(), sizeof(Q3ListBoxPrivate::SortableItem), cmpListBoxItems);
+
+ Q3ListBoxItem *prev = 0;
+ item = 0;
+ if (ascending) {
+ for (i = 0; i < (int)count(); ++i) {
+ item = items[i].item;
+ if (item) {
+ item->p = prev;
+ item->dirty = true;
+ if (item->p)
+ item->p->n = item;
+ item->n = 0;
+ }
+ if (i == 0)
+ d->head = item;
+ prev = item;
+ }
+ } else {
+ for (i = (int)count() - 1; i >= 0 ; --i) {
+ item = items[i].item;
+ if (item) {
+ item->p = prev;
+ item->dirty = true;
+ if (item->p)
+ item->p->n = item;
+ item->n = 0;
+ }
+ if (i == (int)count() - 1)
+ d->head = item;
+ prev = item;
+ }
+ }
+ d->last = item;
+
+ delete [] items;
+
+ // We have to update explicitly in case the current "vieport" overlaps the
+ // new viewport we set (starting at (0,0)).
+ bool haveToUpdate = contentsX() < visibleWidth() || contentsY() < visibleHeight();
+ setContentsPos(0, 0);
+ if (haveToUpdate)
+ updateContents(0, 0, visibleWidth(), visibleHeight());
+}
+
+void Q3ListBox::handleItemChange(Q3ListBoxItem *old, bool shift, bool control)
+{
+ if (d->selectionMode == Single) {
+ // nothing
+ } else if (d->selectionMode == Extended) {
+ if (shift) {
+ selectRange(d->selectAnchor ? d->selectAnchor : old,
+ d->current, false, true, (d->selectAnchor && !control) ? true : false);
+ } else if (!control) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ selectAll(false);
+ blockSignals(block);
+ setSelected(d->current, true);
+ }
+ } else if (d->selectionMode == Multi) {
+ if (shift)
+ selectRange(old, d->current, true, false);
+ }
+}
+
+void Q3ListBox::selectRange(Q3ListBoxItem *from, Q3ListBoxItem *to, bool invert, bool includeFirst, bool clearSel)
+{
+ if (!from || !to)
+ return;
+ if (from == to && !includeFirst)
+ return;
+ Q3ListBoxItem *i = 0;
+ int index =0;
+ int f_idx = -1, t_idx = -1;
+ for (i = d->head; i; i = i->n, index++) {
+ if (i == from)
+ f_idx = index;
+ if (i == to)
+ t_idx = index;
+ if (f_idx != -1 && t_idx != -1)
+ break;
+ }
+ if (f_idx > t_idx) {
+ i = from;
+ from = to;
+ to = i;
+ if (!includeFirst)
+ to = to->prev();
+ } else {
+ if (!includeFirst)
+ from = from->next();
+ }
+
+ bool changed = false;
+ if (clearSel) {
+ for (i = d->head; i && i != from; i = i->n) {
+ if (i->s) {
+ i->s = false;
+ changed = true;
+ updateItem(i);
+ }
+ }
+ for (i = to->n; i; i = i->n) {
+ if (i->s) {
+ i->s = false;
+ changed = true;
+ updateItem(i);
+ }
+ }
+ }
+
+ for (i = from; i; i = i->next()) {
+ if (!invert) {
+ if (!i->s && i->isSelectable()) {
+ i->s = true;
+ changed = true;
+ updateItem(i);
+ }
+ } else {
+ bool sel = !i->s;
+ if (((bool)i->s != sel && sel && i->isSelectable()) || !sel) {
+ i->s = sel;
+ changed = true;
+ updateItem(i);
+ }
+ }
+ if (i == to)
+ break;
+ }
+ if (changed) {
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+#endif
+ }
+}
+
+/*! \reimp */
+void Q3ListBox::changeEvent(QEvent *ev)
+{
+ if (ev->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow() && d->scrollTimer)
+ d->scrollTimer->stop();
+ if (!palette().isEqual(QPalette::Active, QPalette::Inactive))
+ viewport()->update();
+ }
+ Q3ScrollView::changeEvent(ev);
+
+ if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange)
+ triggerUpdate(true);
+}
+
+/*!
+ Returns 0.
+
+ Make your derived classes return their own values for rtti(), and
+ you can distinguish between listbox items. You should use values
+ greater than 1000 preferably a large random number, to allow for
+ extensions to this class.
+*/
+
+int Q3ListBoxItem::rtti() const
+{
+ return RTTI;
+}
+
+/*!
+ \fn bool Q3ListBox::dragSelect() const
+
+ Returns true. Dragging always selects.
+*/
+
+/*!
+ \fn void Q3ListBox::setDragSelect(bool b)
+
+ Does nothing. Dragging always selects. The \a b parameter is ignored.
+*/
+
+/*!
+ \fn bool Q3ListBox::autoScroll() const
+
+ Use dragAutoScroll() instead. This function always returns true.
+*/
+
+/*!
+ \fn void Q3ListBox::setAutoScroll(bool b)
+
+ Use setDragAutoScroll(\a b) instead.
+*/
+
+/*!
+ \fn bool Q3ListBox::autoScrollBar() const
+
+ Use vScrollBarMode() instead. Returns true if the vertical
+ scrollbar mode is \c Auto.
+*/
+
+/*!
+ \fn void Q3ListBox::setAutoScrollBar(bool enable)
+
+ Use setVScrollBarMode() instead.
+
+ If \a enable is true, pass \c Auto as the argument to
+ setVScrollBarMode(); otherwise, pass \c AlwaysOff.
+*/
+
+/*!
+ \fn bool Q3ListBox::scrollBar() const
+
+ Use vScrollBarMode() instead. Returns true if the vertical
+ scrollbar mode is not \c AlwaysOff.
+*/
+
+/*!
+ \fn void Q3ListBox::setScrollBar(bool enable)
+
+ Use setVScrollBarMode() instead.
+
+ If \a enable is true, pass \c AlwaysOn as the argument to
+ setVScrollBarMode(); otherwise, pass \c AlwaysOff.
+*/
+
+/*!
+ \fn bool Q3ListBox::autoBottomScrollBar() const
+
+ Use hScrollBarMode() instead. Returns true if the horizontal
+ scrollbar mode is set to \c Auto.
+*/
+
+/*!
+ \fn void Q3ListBox::setAutoBottomScrollBar(bool enable)
+
+ Use setHScrollBarMode() instead.
+
+ If \a enable is true, pass \c Auto as the argument to
+ setHScrollBarMode(); otherwise, pass \c AlwaysOff.
+*/
+
+/*!
+ \fn bool Q3ListBox::bottomScrollBar() const
+
+ Use hScrollBarMode() instead. Returns true if the horizontal
+ scrollbar mode is not \c AlwaysOff.
+*/
+
+/*!
+ \fn void Q3ListBox::setBottomScrollBar(bool enable)
+
+ Use setHScrollBarMode() instead.
+
+ If \a enable is true, pass \c AlwaysOn as the argument to
+ setHScrollBarMode(); otherwise, pass \c AlwaysOff.
+*/
+
+/*!
+ \fn bool Q3ListBox::smoothScrolling() const
+
+ Returns false. Qt always scrolls smoothly.
+*/
+
+/*!
+ \fn void Q3ListBox::setSmoothScrolling(bool b)
+
+ Does nothing. Qt always scrolls smoothly. The \a b parameter is
+ ignored.
+*/
+
+/*!
+ \fn bool Q3ListBox::autoUpdate() const
+
+ Returns true. Qt always updates automatically.
+*/
+
+/*!
+ \fn void Q3ListBox::setAutoUpdate(bool b)
+
+ Does nothing. Qt always updates automatically. The \a b parameter
+ is ignored.
+*/
+
+/*!
+ \fn void Q3ListBox::setFixedVisibleLines(int lines)
+
+ Use setRowMode(\a lines) instead.
+*/
+
+/*!
+ \fn int Q3ListBox::cellHeight(int i) const
+
+ Use itemHeight(\a i) instead.
+*/
+
+/*!
+ \fn int Q3ListBox::cellHeight() const
+
+ Use itemHeight() instead.
+*/
+
+/*!
+ \fn int Q3ListBox::cellWidth() const
+
+ Use maxItemWidth() instead.
+*/
+
+/*!
+ \fn int Q3ListBox::cellWidth(int i) const
+
+ Use maxItemWidth(\a i) instead.
+*/
+
+/*!
+ \fn int Q3ListBox::numCols() const
+
+ Use numColumns() instead.
+*/
+
+/*!
+ \fn void Q3ListBox::updateCellWidth()
+
+ Does nothing. Qt automatically updates.
+*/
+
+/*!
+ \fn int Q3ListBox::totalWidth() const
+
+ Use contentsWidth() instead.
+*/
+
+/*!
+ \fn int Q3ListBox::totalHeight() const
+
+ Use contentsHeight() instead.
+*/
+
+/*!
+ \fn int Q3ListBox::findItem(int yPos) const
+
+ Use index(itemAt(\a yPos)) instead.
+*/
+
+/*!
+ \fn bool Q3ListBoxItem::selected() const
+
+ Use isSelected() instead. Returns true if isSelected()
+ returns true.
+*/
+
+/*!
+ \fn bool Q3ListBoxItem::current() const
+
+ Use isCurrent() instead. Returns true if isCurrent()
+ returns true.
+*/
+
+/*!
+ \enum Q3ListBox::StringComparisonMode
+
+ This enum type is used to set the string comparison mode when
+ searching for an item. We'll refer to the string being searched
+ as the 'target' string.
+
+ \value CaseSensitive The strings must match case sensitively.
+ \value ExactMatch The target and search strings must match exactly.
+ \value BeginsWith The target string begins with the search string.
+ \value EndsWith The target string ends with the search string.
+ \value Contains The target string contains the search string.
+
+ If you OR these flags together (excluding \c CaseSensitive), the
+ search criteria be applied in the following order: \c ExactMatch,
+ \c BeginsWith, \c EndsWith, \c Contains.
+
+ Matching is case-insensitive unless \c CaseSensitive is set. \c
+ CaseSensitive can be OR-ed with any combination of the other
+ flags.
+
+ \sa ComparisonFlags
+*/
+
+/*!
+ \typedef Q3ListBox::ComparisonFlags
+
+ This typedef is used in Q3IconView's API for values that are OR'd
+ combinations of \l StringComparisonMode values.
+
+ \sa StringComparisonMode
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LISTBOX
diff --git a/src/qt3support/itemviews/q3listbox.h b/src/qt3support/itemviews/q3listbox.h
new file mode 100644
index 0000000..55d7702
--- /dev/null
+++ b/src/qt3support/itemviews/q3listbox.h
@@ -0,0 +1,429 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3LISTBOX_H
+#define Q3LISTBOX_H
+
+#include <Qt3Support/q3scrollview.h>
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_LISTBOX
+
+class Q3ListBoxPrivate;
+class Q3ListBoxItem;
+class QString;
+class QStringList;
+
+class Q_COMPAT_EXPORT Q3ListBox : public Q3ScrollView
+{
+ friend class Q3ListBoxItem;
+ friend class Q3ListBoxPrivate;
+
+ Q_OBJECT
+ Q_ENUMS(SelectionMode LayoutMode)
+ Q_PROPERTY(uint count READ count)
+ Q_PROPERTY(int numItemsVisible READ numItemsVisible)
+ Q_PROPERTY(int currentItem READ currentItem WRITE setCurrentItem USER true)
+ Q_PROPERTY(QString currentText READ currentText)
+ Q_PROPERTY(int topItem READ topItem WRITE setTopItem DESIGNABLE false)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode)
+ Q_PROPERTY(bool multiSelection READ isMultiSelection WRITE setMultiSelection DESIGNABLE false)
+ Q_PROPERTY(LayoutMode columnMode READ columnMode WRITE setColumnMode)
+ Q_PROPERTY(LayoutMode rowMode READ rowMode WRITE setRowMode)
+ Q_PROPERTY(int numColumns READ numColumns)
+ Q_PROPERTY(int numRows READ numRows)
+ Q_PROPERTY(bool variableWidth READ variableWidth WRITE setVariableWidth)
+ Q_PROPERTY(bool variableHeight READ variableHeight WRITE setVariableHeight)
+
+public:
+ Q3ListBox(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0 );
+ ~Q3ListBox();
+
+ uint count() const;
+
+ void insertStringList(const QStringList&, int index=-1);
+// ### fix before Qt 4.0
+#if 0
+ void insertStrList(const QStrList *, int index=-1);
+ void insertStrList(const QStrList &, int index=-1);
+#endif
+ void insertStrList(const char **,
+ int numStrings=-1, int index=-1);
+
+ void insertItem(const Q3ListBoxItem *, int index=-1);
+ void insertItem(const Q3ListBoxItem *, const Q3ListBoxItem *after);
+ void insertItem(const QString &text, int index=-1);
+ void insertItem(const QPixmap &pixmap, int index=-1);
+ void insertItem(const QPixmap &pixmap, const QString &text, int index=-1);
+
+ void removeItem(int index);
+
+ QString text(int index) const;
+ const QPixmap *pixmap(int index) const;
+
+ void changeItem(const Q3ListBoxItem *, int index);
+ void changeItem(const QString &text, int index);
+ void changeItem(const QPixmap &pixmap, int index);
+ void changeItem(const QPixmap &pixmap, const QString &text, int index);
+
+ void takeItem(const Q3ListBoxItem *);
+
+ int numItemsVisible() const;
+
+ int currentItem() const;
+ QString currentText() const { return text(currentItem()); }
+ virtual void setCurrentItem(int index);
+ virtual void setCurrentItem(Q3ListBoxItem *);
+ void centerCurrentItem() { ensureCurrentVisible(); }
+ int topItem() const;
+ virtual void setTopItem(int index);
+ virtual void setBottomItem(int index);
+
+ long maxItemWidth() const;
+
+ enum SelectionMode { Single, Multi, Extended, NoSelection };
+ virtual void setSelectionMode(SelectionMode);
+ SelectionMode selectionMode() const;
+
+ void setMultiSelection(bool multi);
+ bool isMultiSelection() const;
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ virtual void setSelected(Q3ListBoxItem *, bool);
+ void setSelected(int, bool);
+ bool isSelected(int) const;
+ bool isSelected(const Q3ListBoxItem *) const;
+ Q3ListBoxItem* selectedItem() const;
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ Q3ListBoxItem *item(int index) const;
+ int index(const Q3ListBoxItem *) const;
+
+ enum StringComparisonMode {
+ CaseSensitive = 0x00001, // 0 0001
+ BeginsWith = 0x00002, // 0 0010
+ EndsWith = 0x00004, // 0 0100
+ Contains = 0x00008, // 0 1000
+ ExactMatch = 0x00010 // 1 0000
+ };
+ typedef uint ComparisonFlags;
+ Q3ListBoxItem *findItem(const QString &text, ComparisonFlags compare = BeginsWith) const;
+
+ void triggerUpdate(bool doLayout);
+
+ bool itemVisible(int index);
+ bool itemVisible(const Q3ListBoxItem *);
+
+ enum LayoutMode { FixedNumber,
+ FitToWidth, FitToHeight = FitToWidth,
+ Variable };
+ virtual void setColumnMode(LayoutMode);
+ virtual void setColumnMode(int);
+ virtual void setRowMode(LayoutMode);
+ virtual void setRowMode(int);
+
+ LayoutMode columnMode() const;
+ LayoutMode rowMode() const;
+
+ int numColumns() const;
+ int numRows() const;
+
+ bool variableWidth() const;
+ virtual void setVariableWidth(bool);
+
+ bool variableHeight() const;
+ virtual void setVariableHeight(bool);
+
+ void viewportPaintEvent(QPaintEvent *);
+
+ bool dragSelect() const { return true; }
+ void setDragSelect(bool) {}
+ bool autoScroll() const { return true; }
+ void setAutoScroll(bool) {}
+ bool autoScrollBar() const { return vScrollBarMode() == Auto; }
+ void setAutoScrollBar(bool enable) { setVScrollBarMode(enable ? Auto : AlwaysOff); }
+ bool scrollBar() const { return vScrollBarMode() != AlwaysOff; }
+ void setScrollBar(bool enable) { setVScrollBarMode(enable ? AlwaysOn : AlwaysOff); }
+ bool autoBottomScrollBar() const { return hScrollBarMode() == Auto; }
+ void setAutoBottomScrollBar(bool enable) { setHScrollBarMode(enable ? Auto : AlwaysOff); }
+ bool bottomScrollBar() const { return hScrollBarMode() != AlwaysOff; }
+ void setBottomScrollBar(bool enable) { setHScrollBarMode(enable ? AlwaysOn : AlwaysOff); }
+ bool smoothScrolling() const { return false; }
+ void setSmoothScrolling(bool) {}
+ bool autoUpdate() const { return true; }
+ void setAutoUpdate(bool) {}
+ void setFixedVisibleLines(int lines) { setRowMode(lines); }
+ int inSort(const Q3ListBoxItem *);
+ int inSort(const QString& text);
+ int cellHeight(int i) const { return itemHeight(i); }
+ int cellHeight() const { return itemHeight(); }
+ int cellWidth() const { return maxItemWidth(); }
+ int cellWidth(int i) const { Q_ASSERT(i==0); Q_UNUSED(i) return maxItemWidth(); }
+ int numCols() const { return numColumns(); }
+
+ int itemHeight(int index = 0) const;
+ Q3ListBoxItem * itemAt(const QPoint &) const;
+
+ QRect itemRect(Q3ListBoxItem *item) const;
+
+ Q3ListBoxItem *firstItem() const;
+
+ void sort(bool ascending = true);
+
+public Q_SLOTS:
+ void clear();
+ virtual void ensureCurrentVisible();
+ virtual void clearSelection();
+ virtual void selectAll(bool select);
+ virtual void invertSelection();
+
+Q_SIGNALS:
+ void highlighted(int index);
+ void selected(int index);
+ void highlighted(const QString &);
+ void selected(const QString &);
+ void highlighted(Q3ListBoxItem *);
+ void selected(Q3ListBoxItem *);
+
+ void selectionChanged();
+ void selectionChanged(Q3ListBoxItem *);
+ void currentChanged(Q3ListBoxItem *);
+ void clicked(Q3ListBoxItem *);
+ void clicked(Q3ListBoxItem *, const QPoint &);
+ void pressed(Q3ListBoxItem *);
+ void pressed(Q3ListBoxItem *, const QPoint &);
+
+ void doubleClicked(Q3ListBoxItem *);
+ void returnPressed(Q3ListBoxItem *);
+ void rightButtonClicked(Q3ListBoxItem *, const QPoint &);
+ void rightButtonPressed(Q3ListBoxItem *, const QPoint &);
+ void mouseButtonPressed(int, Q3ListBoxItem*, const QPoint&);
+ void mouseButtonClicked(int, Q3ListBoxItem*, const QPoint&);
+
+ void contextMenuRequested(Q3ListBoxItem *, const QPoint &);
+
+ void onItem(Q3ListBoxItem *item);
+ void onViewport();
+
+protected:
+ void changeEvent(QEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseDoubleClickEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void contentsContextMenuEvent(QContextMenuEvent *);
+
+ void keyPressEvent(QKeyEvent *e);
+ void focusInEvent(QFocusEvent *e);
+ void focusOutEvent(QFocusEvent *e);
+ void resizeEvent(QResizeEvent *);
+ void showEvent(QShowEvent *);
+
+ bool eventFilter(QObject *o, QEvent *e);
+
+ void updateItem(int index);
+ void updateItem(Q3ListBoxItem *);
+
+ void updateCellWidth() { }
+ int totalWidth() const { return contentsWidth(); }
+ int totalHeight() const { return contentsHeight(); }
+
+ virtual void paintCell(QPainter *, int row, int col);
+
+ void toggleCurrentItem();
+ bool isRubberSelecting() const;
+
+ void doLayout() const;
+
+ int findItem(int yPos) const { return index(itemAt(QPoint(0,yPos))); }
+
+protected Q_SLOTS:
+ void clearInputString();
+
+private Q_SLOTS:
+ void refreshSlot();
+ void doAutoScroll();
+ void adjustItems();
+
+private:
+ Q_DISABLE_COPY(Q3ListBox)
+
+ void mousePressEventEx(QMouseEvent *);
+ void tryGeometry(int, int) const;
+ int currentRow() const;
+ int currentColumn() const;
+ void updateSelection();
+ void repaintSelection();
+ void drawRubber();
+ void doRubberSelection(const QRect &old, const QRect &rubber);
+ void handleItemChange(Q3ListBoxItem *old, bool shift, bool control);
+ void selectRange(Q3ListBoxItem *from, Q3ListBoxItem *to, bool invert, bool includeFirst, bool clearSel = false);
+
+ void emitChangedSignal(bool);
+
+ int columnAt(int) const;
+ int rowAt(int) const;
+
+ Q3ListBoxPrivate * d;
+
+ static Q3ListBox * changedListBox;
+};
+
+
+class Q_COMPAT_EXPORT Q3ListBoxItem
+{
+public:
+ Q3ListBoxItem(Q3ListBox* listbox = 0);
+ Q3ListBoxItem(Q3ListBox* listbox, Q3ListBoxItem *after);
+ virtual ~Q3ListBoxItem();
+
+ virtual QString text() const;
+ virtual const QPixmap *pixmap() const;
+
+ virtual int height(const Q3ListBox *) const;
+ virtual int width(const Q3ListBox *) const;
+
+ bool isSelected() const { return s; }
+ bool isCurrent() const;
+
+ bool selected() const { return isSelected(); }
+ bool current() const { return isCurrent(); }
+
+ Q3ListBox *listBox() const;
+
+ void setSelectable(bool b) { selectable = b; }
+ bool isSelectable() const { return selectable; }
+
+ Q3ListBoxItem *next() const;
+ Q3ListBoxItem *prev() const;
+
+ virtual int rtti() const;
+ enum { RTTI = 0 };
+
+protected:
+ virtual void paint(QPainter *) = 0;
+ virtual void setText(const QString &text) { txt = text; }
+ void setCustomHighlighting(bool);
+
+private:
+ Q_DISABLE_COPY(Q3ListBoxItem)
+
+ QString txt;
+ uint selectable : 1;
+ uint s : 1;
+ uint dirty:1;
+ uint custom_highlight : 1;
+ uint unused : 28;
+ Q3ListBoxItem * p, * n;
+ Q3ListBox* lbox;
+ friend class Q3ListBox;
+ friend class Q3ListBoxPrivate;
+ friend class Q3ComboBox;
+ friend class Q3ComboBoxPopupItem;
+};
+
+
+class Q_COMPAT_EXPORT Q3ListBoxText : public Q3ListBoxItem
+{
+public:
+ Q3ListBoxText(Q3ListBox* listbox, const QString & text=QString());
+ Q3ListBoxText(const QString & text=QString());
+ Q3ListBoxText(Q3ListBox* listbox, const QString & text, Q3ListBoxItem *after);
+ ~Q3ListBoxText();
+
+ int height(const Q3ListBox *) const;
+ int width(const Q3ListBox *) const;
+
+ int rtti() const;
+ enum { RTTI = 1 };
+
+protected:
+ void paint(QPainter *);
+
+private:
+ Q_DISABLE_COPY(Q3ListBoxText)
+};
+
+
+class Q_COMPAT_EXPORT Q3ListBoxPixmap : public Q3ListBoxItem
+{
+public:
+ Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &);
+ Q3ListBoxPixmap(const QPixmap &);
+ Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap & pix, Q3ListBoxItem *after);
+ Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &, const QString&);
+ Q3ListBoxPixmap(const QPixmap &, const QString&);
+ Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap & pix, const QString&, Q3ListBoxItem *after);
+ ~Q3ListBoxPixmap();
+
+ const QPixmap *pixmap() const { return &pm; }
+
+ int height(const Q3ListBox *) const;
+ int width(const Q3ListBox *) const;
+
+ int rtti() const;
+ enum { RTTI = 2 };
+
+protected:
+ void paint(QPainter *);
+
+private:
+ Q_DISABLE_COPY(Q3ListBoxPixmap)
+
+ QPixmap pm;
+};
+
+#endif // QT_NO_LISTBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3LISTBOX_H
diff --git a/src/qt3support/itemviews/q3listview.cpp b/src/qt3support/itemviews/q3listview.cpp
new file mode 100644
index 0000000..005f57b
--- /dev/null
+++ b/src/qt3support/itemviews/q3listview.cpp
@@ -0,0 +1,7953 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include "q3listview.h"
+#ifndef QT_NO_LISTVIEW
+#include "q3tl.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "q3cleanuphandler.h"
+#include "qcursor.h"
+#include "qdatetime.h"
+#include "q3dragobject.h"
+#include "qevent.h"
+#include "qhash.h"
+#include "q3header.h"
+#include "qicon.h"
+#include "qlineedit.h"
+#include "qpainter.h"
+#include "qpixmapcache.h"
+#include "qstack.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "qtooltip.h"
+#include "qdebug.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+const int Unsorted = 16383;
+
+struct Q3ListViewPrivate
+{
+ // classes that are here to avoid polluting the global name space
+
+ // the magical hidden mother of all items
+ class Root: public Q3ListViewItem {
+ public:
+ Root(Q3ListView * parent);
+
+ void setHeight(int);
+ void invalidateHeight();
+ void setup();
+ Q3ListView * theListView() const;
+
+ Q3ListView * lv;
+ };
+
+ // to remember what's on screen
+ class DrawableItem {
+ public:
+ DrawableItem() {}
+ DrawableItem(int level, int ypos, Q3ListViewItem * item)
+ : l(level), y(ypos), i(item) {};
+ int l;
+ int y;
+ Q3ListViewItem * i;
+ };
+
+ // for sorting
+ class SortableItem {
+ public:
+ /*
+ We could be smarter and keep a pointer to the Q3ListView
+ item instead of numCols, col and asc. This would then allow
+ us to use the physical ordering of columns rather than the
+ logical. Microsoft uses the logical ordering, so there is
+ some virtue in doing so, although it prevents the user from
+ choosing the secondary key.
+ */
+ Q3ListViewItem * item;
+ int numCols;
+ int col;
+ bool asc;
+
+ int cmp(const SortableItem& i) const {
+ int diff = item->compare(i.item, col, asc);
+ if (diff == 0 && numCols != 1) {
+ for (int j = 0; j < numCols; j++) {
+ if (j != col) {
+ diff = item->compare(i.item, j, asc);
+ if (diff != 0)
+ break;
+ }
+ }
+ }
+ return diff;
+ }
+ bool operator<(const SortableItem& i) const { return cmp(i) < 0; }
+ bool operator<=(const SortableItem& i) const { return cmp(i) <= 0; }
+ bool operator>(const SortableItem& i) const { return cmp(i) > 0; }
+ };
+
+ class ItemColumnInfo {
+ public:
+ ItemColumnInfo(): pm(0), next(0), truncated(false), dirty(false), allow_rename(false), width(0) {}
+ ~ItemColumnInfo() { delete pm; delete next; }
+ QString text, tmpText;
+ QPixmap * pm;
+ ItemColumnInfo * next;
+ uint truncated : 1;
+ uint dirty : 1;
+ uint allow_rename : 1;
+ int width;
+ };
+
+ class ViewColumnInfo {
+ public:
+ ViewColumnInfo(): align(Qt::AlignAuto), sortable(true), next(0) {}
+ ~ViewColumnInfo() { delete next; }
+ int align;
+ bool sortable;
+ ViewColumnInfo * next;
+ };
+
+ // private variables used in Q3ListView
+ ViewColumnInfo * vci;
+ Q3Header * h;
+ Root * r;
+ uint rootIsExpandable : 1;
+ int margin;
+
+ Q3ListViewItem * focusItem, *highlighted, *oldFocusItem;
+
+ QTimer * timer;
+ QTimer * dirtyItemTimer;
+ QTimer * visibleTimer;
+ int levelWidth;
+
+ // the list of drawables, and the range drawables covers entirely
+ // (it may also include a few items above topPixel)
+ QList<DrawableItem> drawables;
+ int topPixel;
+ int bottomPixel;
+
+ QList<const Q3ListViewItem *> dirtyItems;
+
+ Q3ListView::SelectionMode selectionMode;
+
+ // Per-column structure for information not in the Q3Header
+ struct Column {
+ Q3ListView::WidthMode wmode;
+ };
+ QVector<Column> column;
+
+ // suggested height for the items
+ int fontMetricsHeight;
+ int minLeftBearing, minRightBearing;
+ int ellipsisWidth;
+
+ // currently typed prefix for the keyboard interface, and the time
+ // of the last key-press
+ QString currentPrefix;
+ QTime currentPrefixTime;
+
+ // holds a list of iterators
+ QList<Q3ListViewItemIterator *> iterators;
+ Q3ListViewItem *pressedItem, *selectAnchor;
+
+ QTimer *scrollTimer;
+ QTimer *renameTimer;
+ QTimer *autoopenTimer;
+
+ // sort column and order #### may need to move to Q3Header [subclass]
+ int sortcolumn;
+ bool ascending :1;
+ bool sortIndicator :1;
+ // whether to select or deselect during this mouse press.
+ bool allColumnsShowFocus :1;
+ bool select :1;
+
+ // true if the widget should take notice of mouseReleaseEvent
+ bool buttonDown :1;
+ // true if the widget should ignore a double-click
+ bool ignoreDoubleClick :1;
+
+ bool clearing :1;
+ bool pressedSelected :1;
+ bool pressedEmptyArea :1;
+
+ bool toolTips :1;
+ bool fullRepaintOnComlumnChange:1;
+ bool updateHeader :1;
+
+ bool startEdit : 1;
+ bool ignoreEditAfterFocus : 1;
+ bool inMenuMode :1;
+
+ Q3ListView::RenameAction defRenameAction;
+
+ Q3ListViewItem *startDragItem;
+ QPoint dragStartPos;
+ int pressedColumn;
+ Q3ListView::ResizeMode resizeMode;
+};
+
+Q_DECLARE_TYPEINFO(Q3ListViewPrivate::DrawableItem, Q_PRIMITIVE_TYPE);
+
+// these should probably be in Q3ListViewPrivate, for future thread safety
+static bool activatedByClick;
+static QPoint activatedP;
+
+#ifndef QT_NO_ACCESSIBILITY
+static int indexOfItem(Q3ListViewItem *item)
+{
+ if (!QAccessible::isActive())
+ return 0;
+
+ static Q3ListViewItem *lastItem = 0;
+ static int lastIndex = 0;
+
+ if (!item || !item->listView())
+ return 0;
+
+ if (item == lastItem)
+ return lastIndex;
+
+ lastItem = item;
+ int index = 1;
+
+ Q3ListViewItemIterator it(item->listView());
+ while (it.current()) {
+ if (it.current() == item) {
+ lastIndex = index;
+ return index;
+ }
+ ++it;
+ ++index;
+ }
+ lastIndex = 0;
+ return 0;
+}
+#endif
+
+/*!
+ Creates a string with ... like "Trollte..." or "...olltech", depending on the alignment.
+*/
+static QString qEllipsisText(const QString &org, const QFontMetrics &fm, int width, int align)
+{
+ int ellWidth = fm.width(QLatin1String("..."));
+ QString text = QString::fromLatin1("");
+ int i = 0;
+ int len = org.length();
+ int offset = (align & Qt::AlignRight) ? (len-1) - i : i;
+ while (i < len && fm.width(text + org[offset]) + ellWidth < width) {
+ if (align & Qt::AlignRight)
+ text.prepend(org[offset]);
+ else
+ text += org[offset];
+ offset = (align & Qt::AlignRight) ? (len-1) - ++i : ++i;
+ }
+ if (text.isEmpty())
+ text = (align & Qt::AlignRight) ? org.right(1) : text = org.left(1);
+ if (align & Qt::AlignRight)
+ text.prepend(QLatin1String("..."));
+ else
+ text += QLatin1String("...");
+ return text;
+}
+
+/*!
+ \class Q3ListViewItem
+ \brief The Q3ListViewItem class implements a list view item.
+
+ \compat
+
+ A list view item is a multi-column object capable of displaying
+ itself in a Q3ListView.
+
+ The easiest way to use Q3ListViewItem is to construct one with a
+ few constant strings, and either a Q3ListView or another
+ Q3ListViewItem as parent.
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 0
+ We've discarded the pointers to the items since we can still access
+ them via their parent \e listView. By default, Q3ListView sorts its
+ items; this can be switched off with Q3ListView::setSorting(-1).
+
+ The parent must be another Q3ListViewItem or a Q3ListView. If the
+ parent is a Q3ListView, the item becomes a top-level item within
+ that Q3ListView. If the parent is another Q3ListViewItem, the item
+ becomes a child of that list view item.
+
+ If you keep the pointer, you can set or change the texts using
+ setText(), add pixmaps using setPixmap(), change its mode using
+ setSelectable(), setSelected(), setOpen() and setExpandable().
+ You'll also be able to change its height using setHeight(), and
+ traverse its sub-items. You don't have to keep the pointer since
+ you can get a pointer to any Q3ListViewItem in a Q3ListView using
+ Q3ListView::selectedItem(), Q3ListView::currentItem(),
+ Q3ListView::firstChild(), Q3ListView::lastItem() and
+ Q3ListView::findItem().
+
+ If you call \c delete on a list view item, it will be deleted as
+ expected, and as usual for \l{QObject}s, if it has any child items
+ (to any depth), all these will be deleted too.
+
+ \l{Q3CheckListItem}s are list view items that have a checkbox or
+ radio button and can be used in place of plain Q3ListViewItems.
+
+ You can traverse the tree as if it were a doubly-linked list using
+ itemAbove() and itemBelow(); they return pointers to the items
+ directly above and below this item on the screen (even if none of
+ them are actually visible at the moment).
+
+ Here's how to traverse all of an item's children (but not its
+ children's children, etc.):
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 1
+
+ If you want to iterate over every item, to any level of depth use
+ an iterator. To iterate over the entire tree, initialize the
+ iterator with the list view itself; to iterate over an item's
+ children (and children's children to any depth), initialize the
+ iterator with the item:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 2
+
+ Note that the order of the children will change when the sorting
+ order changes and is undefined if the items are not visible. You
+ can, however, call enforceSortOrder() at any time; Q3ListView will
+ always call it before it needs to show an item.
+
+ Many programs will need to reimplement Q3ListViewItem. The most
+ commonly reimplemented functions are:
+ \table
+ \header \i Function \i Description
+ \row \i \l text()
+ \i Returns the text in a column. Many subclasses will compute
+ this on the fly.
+ \row \i \l key()
+ \i Used for sorting. The default key() simply calls
+ text(), but judicious use of key() can give you fine
+ control over sorting; for example, QFileDialog
+ reimplements key() to sort by date.
+ \row \i \l setup()
+ \i Called before showing the item and whenever the list
+ view's font changes, for example.
+ \row \i \l activate()
+ \i Called whenever the user clicks on the item or presses
+ Space when the item is the current item.
+ \endtable
+
+ Some subclasses call setExpandable(true) even when they have no
+ children, and populate themselves when setup() or setOpen(true) is
+ called. The \c dirview/dirview.cpp example program uses this
+ technique to start up quickly: The files and subdirectories in a
+ directory aren't inserted into the tree until they're actually
+ needed.
+
+ \img qlistviewitems.png List View Items
+
+ \sa Q3CheckListItem Q3ListView
+*/
+
+/*!
+ \fn int Q3CheckListItem::rtti() const
+
+ Returns 1.
+
+ Make your derived classes return their own values for rtti(), and
+ you can distinguish between list view items. You should use values
+ greater than 1000, to allow for extensions to this class.
+*/
+
+/*!
+ Constructs a new top-level list view item in the Q3ListView \a
+ parent.
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListView * parent)
+{
+ init();
+ parent->insertItem(this);
+}
+
+
+/*!
+ Constructs a new list view item that is a child of \a parent and
+ first in the parent's list of children.
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent)
+{
+ init();
+ parent->insertItem(this);
+}
+
+
+
+
+/*!
+ Constructs an empty list view item that is a child of \a parent
+ and is after item \a after in the parent's list of children. Since
+ \a parent is a Q3ListView the item will be a top-level item.
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after)
+{
+ init();
+ parent->insertItem(this);
+ moveToJustAfter(after);
+}
+
+
+/*!
+ Constructs an empty list view item that is a child of \a parent
+ and is after item \a after in the parent's list of children.
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after)
+{
+ init();
+ parent->insertItem(this);
+ moveToJustAfter(after);
+}
+
+
+
+/*!
+ Constructs a new top-level list view item in the Q3ListView \a
+ parent, with up to eight constant strings, \a label1, \a label2, \a
+ label3, \a label4, \a label5, \a label6, \a label7 and \a label8
+ defining its columns' contents.
+
+ \sa setText()
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListView * parent,
+ const QString &label1,
+ const QString &label2,
+ const QString &label3,
+ const QString &label4,
+ const QString &label5,
+ const QString &label6,
+ const QString &label7,
+ const QString &label8)
+{
+ init();
+ parent->insertItem(this);
+
+ setText(0, label1);
+ setText(1, label2);
+ setText(2, label3);
+ setText(3, label4);
+ setText(4, label5);
+ setText(5, label6);
+ setText(6, label7);
+ setText(7, label8);
+}
+
+
+/*!
+ Constructs a new list view item as a child of the Q3ListViewItem \a
+ parent with up to eight constant strings, \a label1, \a label2, \a
+ label3, \a label4, \a label5, \a label6, \a label7 and \a label8
+ as columns' contents.
+
+ \sa setText()
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent,
+ const QString &label1,
+ const QString &label2,
+ const QString &label3,
+ const QString &label4,
+ const QString &label5,
+ const QString &label6,
+ const QString &label7,
+ const QString &label8)
+{
+ init();
+ parent->insertItem(this);
+
+ setText(0, label1);
+ setText(1, label2);
+ setText(2, label3);
+ setText(3, label4);
+ setText(4, label5);
+ setText(5, label6);
+ setText(6, label7);
+ setText(7, label8);
+}
+
+/*!
+ Constructs a new list view item in the Q3ListView \a parent that is
+ included after item \a after and that has up to eight column
+ texts, \a label1, \a label2, \a label3, \a label4, \a label5, \a
+ label6, \a label7 and\a label8.
+
+ Note that the order is changed according to Q3ListViewItem::key()
+ unless the list view's sorting is disabled using
+ Q3ListView::setSorting(-1).
+
+ \sa setText()
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after,
+ const QString &label1,
+ const QString &label2,
+ const QString &label3,
+ const QString &label4,
+ const QString &label5,
+ const QString &label6,
+ const QString &label7,
+ const QString &label8)
+{
+ init();
+ parent->insertItem(this);
+ moveToJustAfter(after);
+
+ setText(0, label1);
+ setText(1, label2);
+ setText(2, label3);
+ setText(3, label4);
+ setText(4, label5);
+ setText(5, label6);
+ setText(6, label7);
+ setText(7, label8);
+}
+
+
+/*!
+ Constructs a new list view item as a child of the Q3ListViewItem \a
+ parent. It is inserted after item \a after and may contain up to
+ eight strings, \a label1, \a label2, \a label3, \a label4, \a
+ label5, \a label6, \a label7 and \a label8 as column entries.
+
+ Note that the order is changed according to Q3ListViewItem::key()
+ unless the list view's sorting is disabled using
+ Q3ListView::setSorting(-1).
+
+ \sa setText()
+*/
+
+Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after,
+ const QString &label1,
+ const QString &label2,
+ const QString &label3,
+ const QString &label4,
+ const QString &label5,
+ const QString &label6,
+ const QString &label7,
+ const QString &label8)
+{
+ init();
+ parent->insertItem(this);
+ moveToJustAfter(after);
+
+ setText(0, label1);
+ setText(1, label2);
+ setText(2, label3);
+ setText(3, label4);
+ setText(4, label5);
+ setText(5, label6);
+ setText(6, label7);
+ setText(7, label8);
+}
+
+/*!
+ Sorts all this item's child items using the current sorting
+ configuration (sort column and direction).
+
+ \sa enforceSortOrder()
+*/
+
+void Q3ListViewItem::sort()
+{
+ if (!listView())
+ return;
+ lsc = Unsorted;
+ enforceSortOrder();
+ listView()->triggerUpdate();
+}
+
+/*!
+ Returns 0.
+
+ Make your derived classes return their own values for rtti(), so
+ that you can distinguish between different kinds of list view
+ items. You should use values greater than 1000 to allow for
+ extensions to this class.
+*/
+
+int Q3ListViewItem::rtti() const
+{
+ return RTTI;
+}
+
+/*
+ Performs the initializations that's common to the constructors.
+*/
+
+void Q3ListViewItem::init()
+{
+ ownHeight = 0;
+ maybeTotalHeight = -1;
+ open = false;
+
+ nChildren = 0;
+ parentItem = 0;
+ siblingItem = childItem = 0;
+
+ columns = 0;
+
+ selected = 0;
+ selectable = true;
+
+ lsc = Unsorted;
+ lso = true; // unsorted in ascending order :)
+ configured = false;
+ expandable = false;
+ selectable = true;
+ is_root = false;
+ allow_drag = false;
+ allow_drop = false;
+ visible = true;
+ renameBox = 0;
+ enabled = true;
+ mlenabled = false;
+}
+
+/*!
+ If \a b is true, the item is made visible; otherwise it is hidden.
+
+ If the item is not visible, itemAbove() and itemBelow() will never
+ return this item, although you still can reach it by using e.g.
+ Q3ListViewItemIterator.
+*/
+
+void Q3ListViewItem::setVisible(bool b)
+{
+ if (b == (bool)visible)
+ return;
+ Q3ListView *lv = listView();
+ if (!lv)
+ return;
+ if (b && parent() && !parent()->isVisible())
+ return;
+ visible = b;
+ configured = false;
+ setHeight(0);
+ invalidateHeight();
+ if (parent())
+ parent()->invalidateHeight();
+ else
+ lv->d->r->invalidateHeight();
+ for (Q3ListViewItem *i = childItem; i; i = i->siblingItem)
+ i->setVisible(b);
+ if (lv)
+ lv->triggerUpdate();
+}
+
+/*!
+ Returns true if the item is visible; otherwise returns false.
+
+ \sa setVisible()
+*/
+
+bool Q3ListViewItem::isVisible() const
+{
+ return (bool)visible;
+}
+
+/*!
+ If \a b is true, this item can be in-place renamed in the column
+ \a col by the user; otherwise it cannot be renamed in-place.
+*/
+
+void Q3ListViewItem::setRenameEnabled(int col, bool b)
+{
+ Q3ListViewPrivate::ItemColumnInfo * l = (Q3ListViewPrivate::ItemColumnInfo*)columns;
+ if (!l) {
+ l = new Q3ListViewPrivate::ItemColumnInfo;
+ columns = (void*)l;
+ }
+ for(int c = 0; c < col; c++) {
+ if (!l->next)
+ l->next = new Q3ListViewPrivate::ItemColumnInfo;
+ l = l->next;
+ }
+
+ if (!l)
+ return;
+ l->allow_rename = b;
+}
+
+/*!
+ Returns true if this item can be in-place renamed in column \a
+ col; otherwise returns false.
+*/
+
+bool Q3ListViewItem::renameEnabled(int col) const
+{
+ Q3ListViewPrivate::ItemColumnInfo * l = (Q3ListViewPrivate::ItemColumnInfo*)columns;
+ if (!l)
+ return false;
+
+ while(col && l) {
+ l = l->next;
+ col--;
+ }
+
+ if (!l)
+ return false;
+ return (bool)l->allow_rename;
+}
+
+/*!
+ If \a b is true the item is enabled; otherwise it is disabled.
+ Disabled items are drawn differently (e.g. grayed-out) and are not
+ accessible by the user.
+*/
+
+void Q3ListViewItem::setEnabled(bool b)
+{
+ if ((bool)enabled == b)
+ return;
+ enabled = b;
+ if (!enabled)
+ selected = false;
+ Q3ListView *lv = listView();
+ if (lv) {
+ lv->triggerUpdate();
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::StateChanged);
+#endif
+ }
+}
+
+/*!
+ Returns true if this item is enabled; otherwise returns false.
+
+ \sa setEnabled()
+*/
+
+bool Q3ListViewItem::isEnabled() const
+{
+ return (bool)enabled;
+}
+
+/*!
+ If in-place renaming of this item is enabled (see
+ renameEnabled()), this function starts renaming the item in column
+ \a col, by creating and initializing an edit box.
+*/
+
+void Q3ListViewItem::startRename(int col)
+{
+ if (!renameEnabled(col))
+ return;
+ if (renameBox)
+ cancelRename(col);
+ Q3ListView *lv = listView();
+ if (!lv)
+ return;
+
+ if (lv->d->renameTimer)
+ lv->d->renameTimer->stop();
+
+ lv->ensureItemVisible(this);
+
+ if (lv->d->timer->isActive()) {
+ // make sure that pending calculations get finished
+ lv->d->timer->stop();
+ lv->updateContents();
+ }
+
+ if (lv->currentItem() && lv->currentItem()->renameBox) {
+ if (lv->d->defRenameAction == Q3ListView::Reject)
+ lv->currentItem()->cancelRename(lv->currentItem()->renameCol);
+ else
+ lv->currentItem()->okRename(lv->currentItem()->renameCol);
+ }
+
+ if (this != lv->currentItem())
+ lv->setCurrentItem(this);
+
+ QRect r = lv->itemRect(this);
+ r = QRect(lv->viewportToContents(r.topLeft()), r.size());
+ r.setLeft(lv->header()->sectionPos(col));
+ r.setWidth(qMin(lv->header()->sectionSize(col) - 1,
+ lv->contentsX() + lv->visibleWidth() - r.left()));
+ if (col == 0)
+ r.setLeft(r.left() + lv->itemMargin() + (depth() + (lv->rootIsDecorated() ? 1 : 0)) * lv->treeStepSize() - 1);
+ if (pixmap(col))
+ r.setLeft(r.left() + pixmap(col)->width());
+ if (r.x() - lv->contentsX() < 0) {
+ lv->scrollBy(r.x() - lv->contentsX(), 0);
+ r.setX(lv->contentsX());
+ } else if ((lv->contentsX() + lv->visibleWidth()) < (r.x() + r.width())) {
+ lv->scrollBy((r.x() + r.width()) - (lv->contentsX() + lv->visibleWidth()), 0);
+ }
+ if (r.width() > lv->visibleWidth())
+ r.setWidth(lv->visibleWidth());
+ renameBox = new QLineEdit(lv->viewport(), "qt_renamebox");
+ renameBox->setFrame(false);
+ renameBox->setText(text(col));
+ renameBox->selectAll();
+ renameBox->installEventFilter(lv);
+ lv->addChild(renameBox, r.x(), r.y());
+ renameBox->resize(r.size());
+ lv->viewport()->setFocusProxy(renameBox);
+ renameBox->setFocus();
+ renameBox->show();
+ renameCol = col;
+}
+
+/*!
+ This function removes the rename box.
+*/
+
+void Q3ListViewItem::removeRenameBox()
+{
+ // Sanity, it should be checked by the functions calling this first anyway
+ Q3ListView *lv = listView();
+ if (!lv || !renameBox)
+ return;
+ const bool resetFocus = lv->viewport()->focusProxy() == renameBox;
+ delete renameBox;
+ renameBox = 0;
+ if (resetFocus) {
+ lv->viewport()->setFocusProxy(lv);
+ lv->setFocus();
+ }
+}
+
+/*!
+ This function is called if the user presses Enter during in-place
+ renaming of the item in column \a col.
+
+ \sa cancelRename()
+*/
+
+void Q3ListViewItem::okRename(int col)
+{
+ Q3ListView *lv = listView();
+ if (!lv || !renameBox)
+ return;
+ setText(col, renameBox->text());
+ removeRenameBox();
+
+ // we set the parent lsc to Unsorted if that column is the sorted one
+ if (parent() && (int)parent()->lsc == col)
+ parent()->lsc = Unsorted;
+
+ emit lv->itemRenamed(this, col);
+ emit lv->itemRenamed(this, col, text(col));
+}
+
+/*!
+ This function is called if the user cancels in-place renaming of
+ this item in column \a col (e.g. by pressing Esc).
+
+ \sa okRename()
+*/
+
+void Q3ListViewItem::cancelRename(int)
+{
+ Q3ListView *lv = listView();
+ if (!lv || !renameBox)
+ return;
+ removeRenameBox();
+}
+
+/*!
+ Destroys the item, deleting all its children and freeing up all
+ allocated resources.
+*/
+
+Q3ListViewItem::~Q3ListViewItem()
+{
+ if (renameBox) {
+ delete renameBox;
+ renameBox = 0;
+ }
+
+ Q3ListView *lv = listView();
+
+ if (lv) {
+ if (lv->d->oldFocusItem == this)
+ lv->d->oldFocusItem = 0;
+ if (lv->d->focusItem == this)
+ lv->d->focusItem = 0;
+ if (lv->d->highlighted == this)
+ lv->d->highlighted = 0;
+ if (lv->d->pressedItem == this)
+ lv->d->pressedItem = 0;
+ if (lv->d->selectAnchor == this)
+ lv->d->selectAnchor = 0;
+ for (int j = 0; j < lv->d->iterators.size(); ++j) {
+ Q3ListViewItemIterator *i = lv->d->iterators.at(j);
+ if (i->current() == this)
+ i->currentRemoved();
+ }
+ }
+
+ if (parentItem)
+ parentItem->takeItem(this);
+ Q3ListViewItem * i = childItem;
+ childItem = 0;
+ while (i) {
+ i->parentItem = 0;
+ Q3ListViewItem * n = i->siblingItem;
+ delete i;
+ i = n;
+ }
+ delete (Q3ListViewPrivate::ItemColumnInfo *)columns;
+}
+
+
+/*!
+ If \a b is true each of the item's columns may contain multiple
+ lines of text; otherwise each of them may only contain a single
+ line.
+*/
+
+void Q3ListViewItem::setMultiLinesEnabled(bool b)
+{
+ mlenabled = b;
+}
+
+/*!
+ Returns true if the item can display multiple lines of text in its
+ columns; otherwise returns false.
+*/
+
+bool Q3ListViewItem::multiLinesEnabled() const
+{
+ return mlenabled;
+}
+
+/*!
+ If \a allow is true, the list view starts a drag (see
+ Q3ListView::dragObject()) when the user presses and moves the mouse
+ on this item.
+*/
+
+
+void Q3ListViewItem::setDragEnabled(bool allow)
+{
+ allow_drag = (uint)allow;
+}
+
+/*!
+ If \a allow is true, the list view accepts drops onto the item;
+ otherwise drops are not allowed.
+*/
+
+void Q3ListViewItem::setDropEnabled(bool allow)
+{
+ allow_drop = (uint)allow;
+}
+
+/*!
+ Returns true if this item can be dragged; otherwise returns false.
+
+ \sa setDragEnabled()
+*/
+
+bool Q3ListViewItem::dragEnabled() const
+{
+ return (bool)allow_drag;
+}
+
+/*!
+ Returns true if this item accepts drops; otherwise returns false.
+
+ \sa setDropEnabled(), acceptDrop()
+*/
+
+bool Q3ListViewItem::dropEnabled() const
+{
+ return (bool)allow_drop;
+}
+
+/*!
+ Returns true if the item can accept drops of type QMimeSource \a
+ mime; otherwise returns false.
+
+ The default implementation does nothing and returns false. A
+ subclass must reimplement this to accept drops.
+*/
+
+bool Q3ListViewItem::acceptDrop(const QMimeSource *) const
+{
+ return false;
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ This function is called when something was dropped on the item. \a e
+ contains all the information about the drop.
+
+ The default implementation does nothing, subclasses may need to
+ reimplement this function.
+*/
+
+void Q3ListViewItem::dropped(QDropEvent *e)
+{
+ Q_UNUSED(e);
+}
+
+#endif
+
+/*!
+ This function is called when a drag enters the item's bounding
+ rectangle.
+
+ The default implementation does nothing, subclasses may need to
+ reimplement this function.
+*/
+
+void Q3ListViewItem::dragEntered()
+{
+}
+
+/*!
+ This function is called when a drag leaves the item's bounding
+ rectangle.
+
+ The default implementation does nothing, subclasses may need to
+ reimplement this function.
+*/
+
+void Q3ListViewItem::dragLeft()
+{
+}
+
+/*!
+ Inserts \a newChild into this list view item's list of children.
+ You should not need to call this function; it is called
+ automatically by the constructor of \a newChild.
+
+ \warning If you are using \c Single selection mode, then you
+ should only insert unselected items.
+*/
+
+void Q3ListViewItem::insertItem(Q3ListViewItem * newChild)
+{
+ Q3ListView *lv = listView();
+ if (lv && lv->currentItem() && lv->currentItem()->renameBox) {
+ if (lv->d->defRenameAction == Q3ListView::Reject)
+ lv->currentItem()->cancelRename(lv->currentItem()->renameCol);
+ else
+ lv->currentItem()->okRename(lv->currentItem()->renameCol);
+ }
+
+ if (!newChild || newChild->parentItem == this)
+ return;
+ if (newChild->parentItem)
+ newChild->parentItem->takeItem(newChild);
+ if (open)
+ invalidateHeight();
+ newChild->siblingItem = childItem;
+ childItem = newChild;
+ nChildren++;
+ newChild->parentItem = this;
+ lsc = Unsorted;
+ newChild->ownHeight = 0;
+ newChild->configured = false;
+
+ if (lv && !lv->d->focusItem) {
+ lv->d->focusItem = lv->firstChild();
+ lv->d->selectAnchor = lv->d->focusItem;
+ lv->repaintItem(lv->d->focusItem);
+ }
+}
+
+
+/*!
+ \fn void Q3ListViewItem::removeItem(Q3ListViewItem *item)
+
+ Removes the given \a item. Use takeItem() instead.
+*/
+
+
+/*!
+ Removes \a item from this object's list of children and causes an
+ update of the screen display. The item is not deleted. You should
+ not normally need to call this function because
+ Q3ListViewItem::~Q3ListViewItem() calls it.
+
+ The normal way to delete an item is to use \c delete.
+
+ If you need to move an item from one place in the hierarchy to
+ another you can use takeItem() to remove the item from the list
+ view and then insertItem() to put the item back in its new
+ position.
+
+ If a taken item is part of a selection in \c Single selection
+ mode, it is unselected and selectionChanged() is emitted. If a
+ taken item is part of a selection in \c Multi or \c Extended
+ selection mode, it remains selected.
+
+ \warning This function leaves \a item and its children in a state
+ where most member functions are unsafe. Only a few functions work
+ correctly on an item in this state, most notably insertItem(). The
+ functions that work on taken items are explicitly documented as
+ such.
+
+ \sa Q3ListViewItem::insertItem()
+*/
+
+void Q3ListViewItem::takeItem(Q3ListViewItem * item)
+{
+ if (!item)
+ return;
+
+ Q3ListView *lv = listView();
+ if (lv && lv->currentItem() && lv->currentItem()->renameBox) {
+ if (lv->d->defRenameAction == Q3ListView::Reject)
+ lv->currentItem()->cancelRename(lv->currentItem()->renameCol);
+ else
+ lv->currentItem()->okRename(lv->currentItem()->renameCol);
+ }
+ bool emit_changed = false;
+ if (lv && !lv->d->clearing) {
+ if (lv->d->oldFocusItem == this)
+ lv->d->oldFocusItem = 0;
+
+ for (int j = 0; j < lv->d->iterators.size(); ++j) {
+ Q3ListViewItemIterator *i = lv->d->iterators.at(j);
+ if (i->current() == item)
+ i->currentRemoved();
+ }
+
+ invalidateHeight();
+
+ if (lv->d && !lv->d->drawables.isEmpty())
+ lv->d->drawables.clear();
+
+ if (!lv->d->dirtyItems.isEmpty()) {
+ if (item->childItem) {
+ lv->d->dirtyItems.clear();
+ lv->d->dirtyItemTimer->stop();
+ lv->triggerUpdate();
+ } else {
+ lv->d->dirtyItems.removeAll(item);
+ }
+ }
+
+ if (lv->d->focusItem) {
+ const Q3ListViewItem * c = lv->d->focusItem;
+ while(c && c != item)
+ c = c->parentItem;
+ if (c == item) {
+ if (lv->selectedItem()) {
+ // for Single, setSelected(false) when selectedItem() is taken
+ lv->selectedItem()->setSelected(false);
+ // we don't emit selectionChanged(0)
+ emit lv->selectionChanged();
+ }
+ if (item->nextSibling())
+ lv->d->focusItem = item->nextSibling();
+ else if (item->itemAbove())
+ lv->d->focusItem = item->itemAbove();
+ else
+ lv->d->focusItem = 0;
+ emit_changed = true;
+ }
+ }
+
+ // reset anchors etc. if they are set to this or any child
+ // items
+ const Q3ListViewItem *ptr = lv->d->selectAnchor;
+ while (ptr && ptr != item)
+ ptr = ptr->parentItem;
+ if (ptr == item)
+ lv->d->selectAnchor = lv->d->focusItem;
+
+ ptr = lv->d->startDragItem;
+ while (ptr && ptr != item)
+ ptr = ptr->parentItem;
+ if (ptr == item)
+ lv->d->startDragItem = 0;
+
+ ptr = lv->d->pressedItem;
+ while (ptr && ptr != item)
+ ptr = ptr->parentItem;
+ if (ptr == item)
+ lv->d->pressedItem = 0;
+
+ ptr = lv->d->highlighted;
+ while (ptr && ptr != item)
+ ptr = ptr->parentItem;
+ if (ptr == item)
+ lv->d->highlighted = 0;
+ }
+
+ nChildren--;
+
+ Q3ListViewItem ** nextChild = &childItem;
+ while(nextChild && *nextChild && item != *nextChild)
+ nextChild = &((*nextChild)->siblingItem);
+
+ if (nextChild && item == *nextChild)
+ *nextChild = (*nextChild)->siblingItem;
+ item->parentItem = 0;
+ item->siblingItem = 0;
+ item->ownHeight = 0;
+ item->maybeTotalHeight = -1;
+ item->configured = false;
+
+ if (emit_changed) {
+ emit lv->currentChanged(lv->d->focusItem);
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(lv->viewport(), 0, QAccessible::Focus);
+#endif
+ }
+}
+
+
+/*!
+ \fn QString Q3ListViewItem::key(int column, bool ascending) const
+
+ Returns a key that can be used for sorting by column \a column.
+ The default implementation returns text(). Derived classes may
+ also incorporate the order indicated by \a ascending into this
+ key, although this is not recommended.
+
+ If you want to sort on non-alphabetical data, e.g. dates, numbers,
+ etc., it is more efficient to reimplement compare().
+
+ \sa compare(), sortChildItems()
+*/
+
+QString Q3ListViewItem::key(int column, bool) const
+{
+ return text(column);
+}
+
+
+/*!
+ Compares this list view item to \a i using the column \a col in \a
+ ascending order. Returns \< 0 if this item is less than \a i, 0 if
+ they are equal and \> 0 if this item is greater than \a i.
+
+ This function is used for sorting.
+
+ The default implementation compares the item keys (key()) using
+ QString::localeAwareCompare(). A reimplementation can use
+ different values and a different comparison function. Here is a
+ reimplementation that uses plain Unicode comparison:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 3
+ We don't recommend using \a ascending so your code can safely
+ ignore it.
+
+ \sa key() QString::localeAwareCompare() QString::compare()
+*/
+
+int Q3ListViewItem::compare(Q3ListViewItem *i, int col, bool ascending) const
+{
+ return key(col, ascending).localeAwareCompare(i->key(col, ascending));
+}
+
+/*!
+ Sorts this item's children using column \a column. This is done in
+ ascending order if \a ascending is true and in descending order if
+ \a ascending is false.
+
+ Asks some of the children to sort their children. (Q3ListView and
+ Q3ListViewItem ensure that all on-screen objects are properly
+ sorted but may avoid or defer sorting other objects in order to be
+ more responsive.)
+
+ \sa key() compare()
+*/
+
+void Q3ListViewItem::sortChildItems(int column, bool ascending)
+{
+ // we try HARD not to sort. if we're already sorted, don't.
+ if (column == (int)lsc && ascending == (bool)lso)
+ return;
+
+ if (column < 0)
+ return;
+
+ lsc = column;
+ lso = ascending;
+
+ const int nColumns = (listView() ? listView()->columns() : 0);
+
+ // and don't sort if we already have the right sorting order
+ if (column > nColumns || childItem == 0)
+ return;
+
+ // If there is just one child, just sort its children
+ if (childItem->siblingItem == 0) {
+ if (childItem->isOpen())
+ childItem->sortChildItems(column, ascending);
+ return;
+ }
+
+ // make an array for qHeapSort()
+ Q3ListViewPrivate::SortableItem * siblings
+ = new Q3ListViewPrivate::SortableItem[nChildren];
+ Q3ListViewItem * s = childItem;
+ int i = 0;
+ while (s && i < nChildren) {
+ siblings[i].numCols = nColumns;
+ siblings[i].col = column;
+ siblings[i].asc = ascending;
+ siblings[i].item = s;
+ s = s->siblingItem;
+ i++;
+ }
+
+ // and sort it.
+ qHeapSort(siblings, siblings + nChildren);
+
+ // build the linked list of siblings, in the appropriate
+ // direction, and finally set this->childItem to the new top
+ // child.
+ if (ascending) {
+ for(i = 0; i < nChildren - 1; i++)
+ siblings[i].item->siblingItem = siblings[i+1].item;
+ siblings[nChildren-1].item->siblingItem = 0;
+ childItem = siblings[0].item;
+ } else {
+ for(i = nChildren - 1; i > 0; i--)
+ siblings[i].item->siblingItem = siblings[i-1].item;
+ siblings[0].item->siblingItem = 0;
+ childItem = siblings[nChildren-1].item;
+ }
+ for (i = 0; i < nChildren; i++) {
+ if (siblings[i].item->isOpen())
+ siblings[i].item->sort();
+ }
+ delete[] siblings;
+}
+
+
+/*!
+ Sets this item's height to \a height pixels. This implicitly
+ changes totalHeight(), too.
+
+ Note that a font change causes this height to be overwritten
+ unless you reimplement setup().
+
+ For best results in Windows style we suggest using an even number
+ of pixels.
+
+ \sa height() totalHeight() isOpen()
+*/
+
+void Q3ListViewItem::setHeight(int height)
+{
+ if (ownHeight != height) {
+ if (visible)
+ ownHeight = height;
+ else
+ ownHeight = 0;
+ invalidateHeight();
+ }
+}
+
+
+/*!
+ Invalidates the cached total height of this item, including all
+ open children.
+
+ \sa setHeight() height() totalHeight()
+*/
+
+void Q3ListViewItem::invalidateHeight()
+{
+ if (maybeTotalHeight < 0)
+ return;
+ maybeTotalHeight = -1;
+ if (parentItem && parentItem->isOpen())
+ parentItem->invalidateHeight();
+}
+
+
+/*!
+ Opens or closes an item, i.e. shows or hides an item's children.
+
+ If \a o is true all child items are shown initially. The user can
+ hide them by clicking the \bold{-} icon to the left of the item.
+ If \a o is false, the children of this item are initially hidden.
+ The user can show them by clicking the \bold{+} icon to the left
+ of the item.
+
+ \sa height() totalHeight() isOpen()
+*/
+
+void Q3ListViewItem::setOpen(bool o)
+{
+ if (o == (bool)open || !enabled)
+ return;
+ open = o;
+
+ // If no children to show simply emit signals and return
+ if (!nChildren) {
+ Q3ListView *lv = listView();
+ if (lv && this != lv->d->r) {
+ if (o)
+ emit lv->expanded(this);
+ else
+ emit lv->collapsed(this);
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::StateChanged);
+#endif
+ }
+ return;
+ }
+ invalidateHeight();
+
+ if (!configured) {
+ Q3ListViewItem * l = this;
+ QStack<Q3ListViewItem *> s;
+ while(l) {
+ if (l->open && l->childItem) {
+ s.push(l->childItem);
+ } else if (l->childItem) {
+ // first invisible child is unconfigured
+ Q3ListViewItem * c = l->childItem;
+ while(c) {
+ c->configured = false;
+ c = c->siblingItem;
+ }
+ }
+ l->configured = true;
+ l->setup();
+ l = (l == this) ? 0 : l->siblingItem;
+ if (!l && !s.isEmpty())
+ l = s.pop();
+ }
+ }
+
+ Q3ListView *lv = listView();
+
+ if (open && lv)
+ enforceSortOrder();
+
+ if (isVisible() && lv && lv->d && !lv->d->drawables.isEmpty())
+ lv->buildDrawableList();
+
+ if (lv && this != lv->d->r) {
+ if (o)
+ emit lv->expanded(this);
+ else
+ emit lv->collapsed(this);
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::StateChanged);
+#endif
+ }
+}
+
+
+/*!
+ This virtual function is called before the first time Q3ListView
+ needs to know the height or any other graphical attribute of this
+ object, and whenever the font, GUI style, or colors of the list
+ view change.
+
+ The default calls widthChanged() and sets the item's height to the
+ height of a single line of text in the list view's font. (If you
+ use icons, multi-line text, etc., you will probably need to call
+ setHeight() yourself or reimplement it.)
+*/
+
+void Q3ListViewItem::setup()
+{
+ widthChanged();
+ Q3ListView *lv = listView();
+
+ int ph = 0;
+ int h = 0;
+ if (lv) {
+ for (int i = 0; i < lv->d->column.size(); ++i) {
+ if (pixmap(i))
+ ph = qMax(ph, pixmap(i)->height());
+ }
+
+ if (mlenabled) {
+ h = ph;
+ for (int c = 0; c < lv->columns(); ++c) {
+ int lines = text(c).count(QLatin1Char('\n')) + 1;
+ int tmph = lv->d->fontMetricsHeight
+ + lv->fontMetrics().lineSpacing() * (lines - 1);
+ h = qMax(h, tmph);
+ }
+ h += 2*lv->itemMargin();
+ } else {
+ h = qMax(lv->d->fontMetricsHeight, ph) + 2*lv->itemMargin();
+ }
+ }
+
+ h = qMax(h, QApplication::globalStrut().height());
+
+ if (h % 2 > 0)
+ h++;
+ setHeight(h);
+}
+
+
+
+
+/*!
+ This virtual function is called whenever the user presses the mouse
+ on this item or presses Space on it.
+
+ \sa activatedPos()
+*/
+
+void Q3ListViewItem::activate()
+{
+}
+
+
+/*!
+ When called from a reimplementation of activate(), this function
+ gives information on how the item was activated. Otherwise the
+ behavior is undefined.
+
+ If activate() was caused by a mouse press, the function sets \a
+ pos to where the user clicked and returns true; otherwise it
+ returns false and does not change \a pos.
+
+ \a pos is relative to the top-left corner of this item.
+
+ \sa activate()
+*/
+
+bool Q3ListViewItem::activatedPos(QPoint &pos)
+{
+ if (activatedByClick)
+ pos = activatedP;
+ return activatedByClick;
+}
+
+
+/*!
+ \fn bool Q3ListViewItem::isSelectable() const
+
+ Returns true if the item is selectable (as it is by default);
+ otherwise returns false
+
+ \sa setSelectable()
+*/
+
+
+/*!
+ Sets this items to be selectable if \a enable is true (the
+ default) or not to be selectable if \a enable is false.
+
+ The user is not able to select a non-selectable item using either
+ the keyboard or the mouse. The application programmer still can
+ though, e.g. using setSelected().
+
+ \sa isSelectable()
+*/
+
+void Q3ListViewItem::setSelectable(bool enable)
+{
+ selectable = enable;
+}
+
+
+/*!
+ \fn bool Q3ListViewItem::isExpandable() const
+
+ Returns true if this item is expandable even when it has no
+ children; otherwise returns false.
+*/
+
+/*!
+ Sets this item to be expandable even if it has no children if \a
+ enable is true, and to be expandable only if it has children if \a
+ enable is false (the default).
+
+ The dirview example uses this in the canonical fashion. It checks
+ whether the directory is empty in setup() and calls
+ setExpandable(true) if not; in setOpen() it reads the contents of
+ the directory and inserts items accordingly. This strategy means
+ that dirview can display the entire file system without reading
+ very much at startup.
+
+ Note that root items are not expandable by the user unless
+ Q3ListView::setRootIsDecorated() is set to true.
+
+ \sa setSelectable()
+*/
+
+void Q3ListViewItem::setExpandable(bool enable)
+{
+ expandable = enable;
+}
+
+
+/*!
+ Makes sure that this object's children are sorted appropriately.
+
+ This only works if every item from the root item down to this item
+ is already sorted.
+
+ \sa sortChildItems()
+*/
+
+void Q3ListViewItem::enforceSortOrder() const
+{
+ Q3ListView *lv = listView();
+ if (!lv || (lv && (lv->d->clearing || lv->d->sortcolumn == Unsorted)))
+ return;
+ if (parentItem &&
+ (parentItem->lsc != lsc || parentItem->lso != lso))
+ ((Q3ListViewItem *)this)->sortChildItems((int)parentItem->lsc,
+ (bool)parentItem->lso);
+ else if (!parentItem &&
+ ((int)lsc != lv->d->sortcolumn || (bool)lso != lv->d->ascending))
+ ((Q3ListViewItem *)this)->sortChildItems(lv->d->sortcolumn, lv->d->ascending);
+}
+
+
+/*!
+ \fn bool Q3ListViewItem::isSelected() const
+
+ Returns true if this item is selected; otherwise returns false.
+
+ \sa setSelected() Q3ListView::setSelected() Q3ListView::selectionChanged()
+*/
+
+
+/*!
+ If \a s is true this item is selected; otherwise it is deselected.
+
+ This function does not maintain any invariants or repaint anything
+ -- Q3ListView::setSelected() does that.
+
+ \sa height() totalHeight()
+*/
+
+void Q3ListViewItem::setSelected(bool s)
+{
+ bool old = selected;
+
+ Q3ListView *lv = listView();
+ if (lv && lv->selectionMode() != Q3ListView::NoSelection) {
+ if (s && isSelectable())
+ selected = true;
+ else
+ selected = false;
+
+#ifndef QT_NO_ACCESSIBILITY
+ if (old != (bool)selected) {
+ int ind = indexOfItem(this);
+ QAccessible::updateAccessibility(lv->viewport(), ind, QAccessible::StateChanged);
+ QAccessible::updateAccessibility(lv->viewport(), ind, selected ? QAccessible::SelectionAdd : QAccessible::SelectionRemove);
+ }
+#else
+ Q_UNUSED(old);
+#endif
+ }
+}
+
+/*!
+ Returns the total height of this object, including any visible
+ children. This height is recomputed lazily and cached for as long
+ as possible.
+
+ Functions which can affect the total height are, setHeight() which
+ is used to set an item's height, setOpen() to show or hide an
+ item's children, and invalidateHeight() to invalidate the cached
+ height.
+
+ \sa height()
+*/
+
+int Q3ListViewItem::totalHeight() const
+{
+ if (!visible)
+ return 0;
+ if (maybeTotalHeight >= 0)
+ return maybeTotalHeight;
+ Q3ListViewItem * that = (Q3ListViewItem *)this;
+ if (!that->configured) {
+ that->configured = true;
+ that->setup(); // ### virtual non-const function called in const
+ }
+ that->maybeTotalHeight = that->ownHeight;
+
+ if (!that->isOpen() || !that->childCount())
+ return that->ownHeight;
+
+ Q3ListViewItem * child = that->childItem;
+ while (child != 0) {
+ that->maybeTotalHeight += child->totalHeight();
+ child = child->siblingItem;
+ }
+ return that->maybeTotalHeight;
+}
+
+
+/*!
+ Returns the text in column \a column, or an empty string if there is
+ no text in that column.
+
+ \sa key() paintCell()
+*/
+
+QString Q3ListViewItem::text(int column) const
+{
+ Q3ListViewPrivate::ItemColumnInfo * l
+ = (Q3ListViewPrivate::ItemColumnInfo*) columns;
+
+ while(column && l) {
+ l = l->next;
+ column--;
+ }
+
+ return l ? l->text : QString();
+}
+
+
+/*!
+ Sets the text in column \a column to \a text, if \a column is a
+ valid column number and \a text is different from the existing
+ text.
+
+ If the text() function has been reimplemented, this function may
+ be a no-op.
+
+ \sa text() key()
+*/
+
+void Q3ListViewItem::setText(int column, const QString &text)
+{
+ if (column < 0)
+ return;
+
+ Q3ListViewPrivate::ItemColumnInfo * l
+ = (Q3ListViewPrivate::ItemColumnInfo*) columns;
+ if (!l) {
+ l = new Q3ListViewPrivate::ItemColumnInfo;
+ columns = (void*)l;
+ }
+ for(int c = 0; c < column; c++) {
+ if (!l->next)
+ l->next = new Q3ListViewPrivate::ItemColumnInfo;
+ l = l->next;
+ }
+ if (l->text == text)
+ return;
+
+ int oldLc = 0;
+ int newLc = 0;
+ if (mlenabled) {
+ if (!l->text.isEmpty())
+ oldLc = l->text.count(QLatin1Char('\n')) + 1;
+ if (!text.isEmpty())
+ newLc = text.count(QLatin1Char('\n')) + 1;
+ }
+
+ l->dirty = true;
+ l->text = text;
+ if (column == (int)lsc)
+ lsc = Unsorted;
+
+ if (mlenabled && oldLc != newLc)
+ setup();
+ else
+ widthChanged(column);
+
+ Q3ListView * lv = listView();
+ if (lv) {
+ lv->triggerUpdate();
+#ifndef QT_NO_ACCESSIBILITY
+ if (lv->isVisible())
+ QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::NameChanged);
+#endif
+ }
+}
+
+
+/*!
+ Sets the pixmap in column \a column to \a pm, if \a pm is non-null
+ and different from the current pixmap, and if \a column is
+ non-negative.
+
+ \sa pixmap() setText()
+*/
+
+void Q3ListViewItem::setPixmap(int column, const QPixmap & pm)
+{
+ if (column < 0)
+ return;
+
+ int oldW = 0;
+ int oldH = 0;
+ if (pixmap(column)) {
+ oldW = pixmap(column)->width();
+ oldH = pixmap(column)->height();
+ }
+
+ Q3ListViewPrivate::ItemColumnInfo * l
+ = (Q3ListViewPrivate::ItemColumnInfo*) columns;
+ if (!l) {
+ l = new Q3ListViewPrivate::ItemColumnInfo;
+ columns = (void*)l;
+ }
+
+ for(int c = 0; c < column; c++) {
+ if (!l->next)
+ l->next = new Q3ListViewPrivate::ItemColumnInfo;
+ l = l->next;
+ }
+
+ if ((pm.isNull() && (!l->pm || l->pm->isNull())) ||
+ (l->pm && pm.serialNumber() == l->pm->serialNumber()))
+ return;
+
+ if (pm.isNull()) {
+ delete l->pm;
+ l->pm = 0;
+ } else {
+ if (l->pm)
+ *(l->pm) = pm;
+ else
+ l->pm = new QPixmap(pm);
+ }
+
+ int newW = 0;
+ int newH = 0;
+ if (pixmap(column)) {
+ newW = pixmap(column)->width();
+ newH = pixmap(column)->height();
+ }
+
+ if (oldW != newW || oldH != newH) {
+ setup();
+ widthChanged(column);
+ invalidateHeight();
+ }
+ Q3ListView *lv = listView();
+ if (lv) {
+ lv->triggerUpdate();
+ }
+}
+
+
+/*!
+ Returns the pixmap for \a column, or 0 if there is no pixmap for
+ \a column.
+
+ \sa setText() setPixmap()
+*/
+
+const QPixmap * Q3ListViewItem::pixmap(int column) const
+{
+ Q3ListViewPrivate::ItemColumnInfo * l
+ = (Q3ListViewPrivate::ItemColumnInfo*) columns;
+
+ while(column && l) {
+ l = l->next;
+ column--;
+ }
+
+ return (l && l->pm) ? l->pm : 0;
+}
+
+
+/*
+ This function paints the contents of one column of an item
+ and aligns it as described by \a align.
+
+ \a p is a QPainter open on the relevant paint device. \a p is
+ translated so (0, 0) is the top-left pixel in the cell and \a
+ width-1, height()-1 is the bottom-right pixel \e in the cell. The
+ other properties of \a p (pen, brush, etc) are undefined. \a pal is
+ the color group to use. \a column is the logical column number
+ within the item that is to be painted; 0 is the column which may
+ contain a tree.
+
+ This function may use Q3ListView::itemMargin() for readability
+ spacing on the left and right sides of data such as text, and
+ should honor isSelected() and Q3ListView::allColumnsShowFocus().
+
+ If you reimplement this function, you should also reimplement
+ width().
+
+ The rectangle to be painted is in an undefined state when this
+ function is called, so you \e must draw on all the pixels. The
+ painter \a p has the right font on entry.
+
+ \sa paintBranches(), Q3ListView::drawContentsOffset()
+*/
+
+static QStyleOptionQ3ListView getStyleOption(const Q3ListView *lv, const Q3ListViewItem *item,
+ bool hierarchy = false)
+{
+ QStyleOptionQ3ListView opt;
+ opt.init(lv);
+ opt.subControls = QStyle::SC_None;
+ opt.activeSubControls = QStyle::SC_None;
+ QWidget *vp = lv->viewport();
+ opt.viewportPalette = vp->palette();
+ opt.viewportBGRole = vp->backgroundRole();
+ opt.itemMargin = lv->itemMargin();
+ opt.sortColumn = 0;
+ opt.treeStepSize = lv->treeStepSize();
+ opt.rootIsDecorated = lv->rootIsDecorated();
+ bool firstItem = true;
+ int y = item ? item->itemPos() : 0;
+ while (item) {
+ QStyleOptionQ3ListViewItem lvi;
+ lvi.height = item->height();
+ lvi.totalHeight = item->totalHeight();
+ lvi.itemY = y;
+ lvi.childCount = item->childCount();
+ lvi.features = QStyleOptionQ3ListViewItem::None;
+ lvi.state = QStyle::State_None;
+ if (item->isEnabled())
+ lvi.state |= QStyle::State_Enabled;
+ if (item->isOpen())
+ lvi.state |= QStyle::State_Open;
+ if (item->isExpandable())
+ lvi.features |= QStyleOptionQ3ListViewItem::Expandable;
+ if (item->multiLinesEnabled())
+ lvi.features |= QStyleOptionQ3ListViewItem::MultiLine;
+ if (item->isVisible())
+ lvi.features |= QStyleOptionQ3ListViewItem::Visible;
+ if (item->parent() && item->parent()->rtti() == 1
+ && static_cast<Q3CheckListItem *>(item->parent())->type() == Q3CheckListItem::Controller)
+ lvi.features |= QStyleOptionQ3ListViewItem::ParentControl;
+ opt.items.append(lvi);
+ // we only care about the children when we are painting the branches
+ // this is only enabled by Q3ListViewItem::paintBranches
+ if (hierarchy) {
+ if (!firstItem) {
+ item = item->nextSibling();
+ } else {
+ firstItem = false;
+ item = item->firstChild();
+ }
+ y += lvi.height;
+ } else {
+ break;
+ }
+ }
+ return opt;
+}
+
+/*!
+ \fn void Q3ListViewItem::paintCell(QPainter *painter, const QColorGroup & cg, int column, int width, int align)
+
+ This virtual function paints the contents of one column of an item
+ and aligns it as described by \a align.
+
+ The \a painter is a Q3Painter open on the relevant paint
+ device. It is translated so (0, 0) is the top-left pixel in the
+ cell and \a width - 1, height() - 1 is the bottom-right pixel \e
+ in the cell. The other properties of the \a painter (pen, brush, etc) are
+ undefined. \a cg is the color group to use. \a column is the
+ logical column number within the item that is to be painted; 0 is
+ the column which may contain a tree.
+
+ This function may use Q3ListView::itemMargin() for readability
+ spacing on the left and right sides of data such as text, and
+ should honor \l isSelected() and
+ Q3ListView::allColumnsShowFocus().
+
+ If you reimplement this function, you should also reimplement \l
+ width().
+
+ The rectangle to be painted is in an undefined state when this
+ function is called, so you \e must draw on all the pixels. The
+ \a painter has the right font on entry.
+
+ \sa paintBranches(), Q3ListView::drawContentsOffset()
+*/
+void Q3ListViewItem::paintCell(QPainter * p, const QColorGroup & cg,
+ int column, int width, int align)
+{
+ // Change width() if you change this.
+
+ QPalette pal = cg;
+ if (!p)
+ return;
+
+ Q3ListView *lv = listView();
+ if (!lv)
+ return;
+ QFontMetrics fm(p->fontMetrics());
+
+ // had, but we _need_ the column info for the ellipsis thingy!!!
+ if (!columns) {
+ for (int i = 0; i < lv->d->column.size(); ++i) {
+ setText(i, text(i));
+ }
+ }
+
+ QString t = text(column);
+
+ if (columns) {
+ Q3ListViewPrivate::ItemColumnInfo *ci = 0;
+ // try until we have a column info....
+ while (!ci) {
+ ci = (Q3ListViewPrivate::ItemColumnInfo*)columns;
+ for (int i = 0; ci && (i < column); ++i)
+ ci = ci->next;
+
+ if (!ci) {
+ setText(column, t);
+ ci = 0;
+ }
+ }
+
+ // if the column width changed and this item was not painted since this change
+ if (ci && (ci->width != width || ci->text != t || ci->dirty)) {
+ ci->text = t;
+ ci->dirty = false;
+ ci->width = width;
+ ci->truncated = false;
+ // if we have to do the ellipsis thingy calc the truncated text
+ int pw = lv->itemMargin()*2 - lv->d->minLeftBearing - lv->d->minRightBearing;
+ pw += pixmap(column) ? pixmap(column)->width() + lv->itemMargin() : 0;
+ if (!mlenabled && fm.width(t) + pw > width) {
+ // take care of arabic shaping in width calculation (lars)
+ ci->truncated = true;
+ ci->tmpText = qEllipsisText(t, fm, width - pw, align);
+ } else if (mlenabled && fm.width(t) + pw > width) {
+ QStringList list = t.split(QLatin1Char('\n'));
+ for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
+ QString z = *it;
+ if (fm.width(z) + pw > width) {
+ ci->truncated = true;
+ *it = qEllipsisText(z, fm, width - pw, align);
+ }
+ }
+
+ if (ci->truncated)
+ ci->tmpText = list.join(QString(QLatin1Char('\n')));
+ }
+ }
+
+ // if we have to draw the ellipsis thingy, use the truncated text
+ if (ci && ci->truncated)
+ t = ci->tmpText;
+ }
+
+ int marg = lv->itemMargin();
+ int r = marg;
+ const QPixmap * icon = pixmap(column);
+
+ const QPalette::ColorRole crole = lv->viewport()->backgroundRole();
+ if (pal.brush(crole) != lv->palette().brush(pal.currentColorGroup(), crole))
+ p->fillRect(0, 0, width, height(), pal.brush(crole));
+ else
+ lv->paintEmptyArea(p, QRect(0, 0, width, height()));
+
+ // (lars) what does this do???
+#if 0 // RS: ####
+ if (align != Qt::AlignLeft)
+ marg -= lv->d->minRightBearing;
+#endif
+ if (isSelected() &&
+ (column == 0 || lv->allColumnsShowFocus())) {
+ p->fillRect(r - marg, 0, qMax(0, width - r + marg), height(),
+ pal.brush(QPalette::Highlight));
+ if (enabled || !lv)
+ p->setPen(pal.highlightedText().color());
+ else if (!enabled && lv)
+ p->setPen(lv->palette().color(QPalette::Disabled, QPalette::HighlightedText));
+ } else {
+ if (enabled || !lv)
+ p->setPen(pal.text().color());
+ else if (!enabled && lv)
+ p->setPen(lv->palette().color(QPalette::Disabled, QPalette::Text));
+ }
+
+
+#if 0
+ bool reverse = QApplication::reverseLayout();
+#else
+ bool reverse = false;
+#endif
+ int iconWidth = 0;
+
+ if (icon) {
+ iconWidth = icon->width() + lv->itemMargin();
+ int xo = r;
+ // we default to Qt::AlignVCenter.
+ int yo = (height() - icon->height()) / 2;
+
+ // I guess we may as well always respect vertical alignment.
+ if (align & Qt::AlignBottom)
+ yo = height() - icon->height();
+ else if (align & Qt::AlignTop)
+ yo = 0;
+
+ // respect horizontal alignment when there is no text for an item.
+ if (text(column).isEmpty()) {
+ if (align & Qt::AlignRight)
+ xo = width - 2 * marg - iconWidth;
+ else if (align & Qt::AlignHCenter)
+ xo = (width - iconWidth) / 2;
+ }
+ if (reverse)
+ xo = width - 2 * marg - iconWidth;
+ p->drawPixmap(xo, yo, *icon);
+ }
+
+ if (!t.isEmpty()) {
+ if (!mlenabled) {
+ if (!(align & Qt::AlignTop || align & Qt::AlignBottom))
+ align |= Qt::AlignVCenter;
+ } else {
+ if (!(align & Qt::AlignVCenter || align & Qt::AlignBottom))
+ align |= Qt::AlignTop;
+ }
+ if (!reverse)
+ r += iconWidth;
+
+ if (!mlenabled) {
+ p->drawText(r, 0, width-marg-r, height(), align, t);
+ } else {
+ p->drawText(r, marg, width-marg-r, height(), align, t);
+ }
+ }
+
+ if (mlenabled && column == 0 && isOpen() && childCount()) {
+ int textheight = fm.size(align, t).height() + 2 * lv->itemMargin();
+ textheight = qMax(textheight, QApplication::globalStrut().height());
+ if (textheight % 2 > 0)
+ textheight++;
+ if (textheight < height()) {
+ int w = lv->treeStepSize() / 2;
+ QStyleOptionQ3ListView opt = getStyleOption(lv, this);
+ opt.rect.setRect(0, textheight, w + 1, height() - textheight + 1);
+ opt.palette = pal;
+ opt.subControls = QStyle::SC_Q3ListViewExpand;
+ opt.activeSubControls = QStyle::SC_All;
+ lv->style()->drawComplexControl(QStyle::CC_Q3ListView, &opt, p, lv);
+ }
+ }
+}
+
+/*!
+ Returns the number of pixels of width required to draw column \a c
+ of list view \a lv, using the metrics \a fm without cropping. The
+ list view containing this item may use this information depending
+ on the Q3ListView::WidthMode settings for the column.
+
+ The default implementation returns the width of the bounding
+ rectangle of the text of column \a c.
+
+ \sa listView() widthChanged() Q3ListView::setColumnWidthMode()
+ Q3ListView::itemMargin()
+*/
+int Q3ListViewItem::width(const QFontMetrics& fm,
+ const Q3ListView* lv, int c) const
+{
+ int w;
+ if (mlenabled)
+ w = fm.size(Qt::AlignVCenter, text(c)).width() + lv->itemMargin() * 2
+ - lv->d->minLeftBearing - lv->d->minRightBearing;
+ else
+ w = fm.width(text(c)) + lv->itemMargin() * 2
+ - lv->d->minLeftBearing - lv->d->minRightBearing;
+ const QPixmap * pm = pixmap(c);
+ if (pm)
+ w += pm->width() + lv->itemMargin(); // ### correct margin stuff?
+ return qMax(w, QApplication::globalStrut().width());
+}
+
+
+/*!
+ Paints a focus indicator on the rectangle \a r using painter \a p
+ and colors \a cg.
+
+ \a p is already clipped.
+
+ \sa paintCell() paintBranches() Q3ListView::setAllColumnsShowFocus()
+*/
+
+void Q3ListViewItem::paintFocus(QPainter *p, const QColorGroup &cg, const QRect &r)
+{
+ QPalette pal = cg;
+ Q3ListView *lv = listView();
+ if (lv) {
+ QStyleOptionFocusRect opt;
+ opt.init(lv);
+ opt.rect = r;
+ opt.palette = pal;
+ opt.state |= QStyle::State_KeyboardFocusChange;
+ if (isSelected()) {
+ opt.state |= QStyle::State_FocusAtBorder;
+ opt.backgroundColor = pal.highlight().color();
+ } else {
+ opt.state |= QStyle::State_None;
+ opt.backgroundColor = pal.base().color();
+ }
+ lv->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, lv);
+ }
+}
+
+
+/*!
+ Paints a set of branches from this item to (some of) its children.
+
+ Painter \a p is set up with clipping and translation so that you
+ can only draw in the rectangle that needs redrawing; \a cg is the
+ color group to use; the update rectangle is at (0, 0) and has size
+ width \a w by height \a h. The top of the rectangle you own is at
+ \a y (which is never greater than 0 but can be outside the window
+ system's allowed coordinate range).
+
+ The update rectangle is in an undefined state when this function
+ is called; this function must draw on \e all of the pixels.
+
+ \sa paintCell(), Q3ListView::drawContentsOffset()
+*/
+
+void Q3ListViewItem::paintBranches(QPainter * p, const QColorGroup & cg,
+ int w, int y, int h)
+{
+ Q3ListView *lv = listView();
+ if (lv)
+ lv->paintEmptyArea(p, QRect(0, 0, w, h));
+ if (!visible || !lv)
+ return;
+ QStyleOptionQ3ListView opt = getStyleOption(lv, this, true);
+ opt.rect.setRect(0, y, w, h);
+ opt.palette = cg;
+ opt.subControls = QStyle::SC_Q3ListViewBranch | QStyle::SC_Q3ListViewExpand;
+ opt.activeSubControls = QStyle::SC_None;
+ lv->style()->drawComplexControl(QStyle::CC_Q3ListView, &opt, p, lv);
+}
+
+
+Q3ListViewPrivate::Root::Root(Q3ListView * parent)
+ : Q3ListViewItem(parent)
+{
+ lv = parent;
+ setHeight(0);
+ setOpen(true);
+}
+
+
+void Q3ListViewPrivate::Root::setHeight(int)
+{
+ Q3ListViewItem::setHeight(0);
+}
+
+
+void Q3ListViewPrivate::Root::invalidateHeight()
+{
+ Q3ListViewItem::invalidateHeight();
+ lv->triggerUpdate();
+}
+
+
+Q3ListView * Q3ListViewPrivate::Root::theListView() const
+{
+ return lv;
+}
+
+
+void Q3ListViewPrivate::Root::setup()
+{
+ // explicitly nothing
+}
+
+
+
+/*!
+\internal
+If called after a mouse click, tells the list view to ignore a
+following double click. This state is reset after the next mouse click.
+*/
+
+void Q3ListViewItem::ignoreDoubleClick()
+{
+ Q3ListView *lv = listView();
+ if (lv)
+ lv->d->ignoreDoubleClick = true;
+}
+
+
+
+/*!
+ \fn void Q3ListView::onItem(Q3ListViewItem *i)
+
+ This signal is emitted when the user moves the mouse cursor onto
+ item \a i, similar to the QWidget::enterEvent() function.
+*/
+
+// ### bug here too? see qiconview.cppp onItem/onViewport
+
+/*!
+ \fn void Q3ListView::onViewport()
+
+ This signal is emitted when the user moves the mouse cursor from
+ an item to an empty part of the list view.
+*/
+
+/*!
+ \enum Q3ListView::SelectionMode
+
+ This enumerated type is used by Q3ListView to indicate how it
+ reacts to selection by the user.
+
+ \value Single When the user selects an item, any already-selected
+ item becomes unselected, and the user cannot unselect the selected
+ item.
+
+ \value Multi When the user selects an item in the usual way, the
+ selection status of that item is toggled and the other items are
+ left alone.
+
+ \value Extended When the user selects an item in the usual way,
+ the selection is cleared and the new item selected. However, if
+ the user presses the Ctrl key when clicking on an item, the
+ clicked item gets toggled and all other items are left untouched.
+ And if the user presses the Shift key while clicking on an item,
+ all items between the current item and the clicked item get
+ selected or unselected, depending on the state of the clicked
+ item. Also, multiple items can be selected by dragging the mouse
+ over them.
+
+ \value NoSelection Items cannot be selected.
+
+ In other words, \c Single is a real single-selection list view, \c
+ Multi a real multi-selection list view, \c Extended is a list view
+ where users can select multiple items but usually want to select
+ either just one or a range of contiguous items, and \c NoSelection
+ is a list view where the user can look but not touch.
+*/
+
+/*!
+ \enum Q3ListView::ResizeMode
+
+ This enum describes how the list view's header adjusts to resize
+ events which affect the width of the list view.
+
+ \value NoColumn The columns do not get resized in resize events.
+
+ \value AllColumns All columns are resized equally to fit the width
+ of the list view.
+
+ \value LastColumn The last column is resized to fit the width of
+ the list view.
+*/
+
+/*!
+ \enum Q3ListView::RenameAction
+
+ This enum describes whether a rename operation is accepted if the
+ rename editor loses focus without the user pressing Enter.
+
+ \value Accept Rename if Enter is pressed or focus is lost.
+
+ \value Reject Discard the rename operation if focus is lost (and
+ Enter has not been pressed).
+*/
+
+/*!
+ \class Q3ListView
+ \brief The Q3ListView class implements a list/tree view.
+
+ \compat
+
+ It can display and control a hierarchy of multi-column items, and
+ provides the ability to add new items at any time. The user may
+ select one or many items (depending on the \c SelectionMode) and
+ sort the list in increasing or decreasing order by any column.
+
+ The simplest pattern of use is to create a Q3ListView, add some
+ column headers using addColumn() and create one or more
+ Q3ListViewItem or Q3CheckListItem objects with the Q3ListView as
+ parent.
+
+ Further nodes can be added to the list view object (the root of the
+ tree) or as child nodes to Q3ListViewItems.
+
+ The main setup functions are:
+ \table
+ \header \i Function \i Action
+ \row \i \l addColumn()
+ \i Adds a column with a text label and perhaps width. Columns
+ are counted from the left starting with column 0.
+ \row \i \l setColumnWidthMode()
+ \i Sets the column to be resized automatically or not.
+ \row \i \l setAllColumnsShowFocus()
+ \i Sets whether items should show keyboard focus using all
+ columns or just column 0. The default is to show focus
+ just using column 0.
+ \row \i \l setRootIsDecorated()
+ \i Sets whether root items can be opened and closed by the
+ user and have open/close decoration to their left. The
+ default is false.
+ \row \i \l setTreeStepSize()
+ \i Sets how many pixels an item's children are indented
+ relative to their parent. The default is 20. This is
+ mostly a matter of taste.
+ \row \i \l setSorting()
+ \i Sets whether the items should be sorted, whether it should
+ be in ascending or descending order, and by what column
+ they should be sorted. By default the list view is sorted
+ by the first column; to switch this off call setSorting(-1).
+ \endtable
+
+ There are several functions for mapping between items and
+ coordinates. itemAt() returns the item at a position on-screen,
+ itemRect() returns the rectangle an item occupies on the screen,
+ and itemPos() returns the position of any item (whether it is
+ on-screen or not). firstChild() returns the list view's first item
+ (not necessarily visible on-screen).
+
+ You can iterate over visible items using
+ Q3ListViewItem::itemBelow(); over a list view's top-level items
+ using Q3ListViewItem::firstChild() and
+ Q3ListViewItem::nextSibling(); or every item using a
+ Q3ListViewItemIterator. See
+ the Q3ListViewItem documentation for examples of traversal.
+
+ An item can be moved amongst its siblings using
+ Q3ListViewItem::moveItem(). To move an item in the hierarchy use
+ takeItem() and insertItem(). Item's (and all their child items)
+ are deleted with \c delete; to delete all the list view's items
+ use clear().
+
+ There are a variety of selection modes described in the
+ Q3ListView::SelectionMode documentation. The default is \c Single
+ selection, which you can change using setSelectionMode().
+
+ Because Q3ListView offers multiple selection it must display
+ keyboard focus and selection state separately. Therefore there are
+ functions both to set the selection state of an item
+ (setSelected()) and to set which item displays keyboard focus
+ (setCurrentItem()).
+
+ Q3ListView emits two groups of signals; one group signals changes
+ in selection/focus state and one indicates selection. The first
+ group consists of selectionChanged() (applicable to all list
+ views), selectionChanged(Q3ListViewItem*) (applicable only to a
+ \c Single selection list view), and currentChanged(Q3ListViewItem*).
+ The second group consists of doubleClicked(Q3ListViewItem*),
+ returnPressed(Q3ListViewItem*),
+ rightButtonClicked(Q3ListViewItem*, const QPoint&, int), etc.
+
+ Note that changing the state of the list view in a slot connected
+ to a list view signal may cause unexpected side effects. If you
+ need to change the list view's state in response to a signal, use
+ a \link QTimer::singleShot() single shot timer\endlink with a
+ time out of 0, and connect this timer to a slot that modifies the
+ list view's state.
+
+ In Motif style, Q3ListView deviates fairly strongly from the look
+ and feel of the Motif hierarchical tree view. This is done mostly
+ to provide a usable keyboard interface and to make the list view
+ look better with a white background.
+
+ If selectionMode() is \c Single (the default) the user can select
+ one item at a time, e.g. by clicking an item with the mouse, see
+ \l Q3ListView::SelectionMode for details.
+
+ The list view can be navigated either using the mouse or the
+ keyboard. Clicking a \bold{-} icon closes an item (hides its
+ children) and clicking a \bold{+} icon opens an item (shows its
+ children). The keyboard controls are these:
+ \table
+ \header \i Keypress \i Action
+ \row \i Home
+ \i Make the first item current and visible.
+ \row \i End
+ \i Make the last item current and visible.
+ \row \i Page Up
+ \i Make the item above the top visible item current and visible.
+ \row \i Page Down
+ \i Make the item below the bottom visible item current and visible.
+ \row \i Up Arrow
+ \i Make the item above the current item current and visible.
+ \row \i Down Arrow
+ \i Make the item below the current item current and visible.
+ \row \i Left Arrow
+ \i If the current item is closed (\bold{+} icon) or has no
+ children, make its parent item current and visible. If the
+ current item is open (\bold{-} icon) close it, i.e. hide its
+ children. Exception: if the current item is the first item
+ and is closed and the horizontal scroll bar is offset to
+ the right the list view will be scrolled left.
+ \row \i Right Arrow
+ \i If the current item is closed (\bold{+} icon) and has
+ children, the item is opened. If the current item is
+ opened (\bold{-} icon) and has children the item's first
+ child is made current and visible. If the current item has
+ no children the list view is scrolled right.
+ \endtable
+
+ If the user starts typing letters with the focus in the list view
+ an incremental search will occur. For example if the user types
+ 'd' the current item will change to the first item that begins
+ with the letter 'd'; if they then type 'a', the current item will
+ change to the first item that begins with 'da', and so on. If no
+ item begins with the letters they type the current item doesn't
+ change.
+
+ Note that the list view's size hint is calculated taking into
+ account the height \e and width to produce a nice aspect ratio.
+ This may mean that you need to reimplement sizeHint() in some
+ cases.
+
+ \warning The list view assumes ownership of all list view items
+ and will delete them when it does not need them any more.
+
+ \sa Q3ListViewItem Q3CheckListItem
+*/
+
+/*!
+ \fn void Q3ListView::itemRenamed(Q3ListViewItem * item, int col)
+
+ \overload
+
+ This signal is emitted when \a item has been renamed, e.g. by
+ in-place renaming, in column \a col.
+
+ \sa Q3ListViewItem::setRenameEnabled()
+*/
+
+/*!
+ \fn void Q3ListView::itemRenamed(Q3ListViewItem * item, int col, const QString &text)
+
+ This signal is emitted when \a item has been renamed to \a text,
+ e.g. by in in-place renaming, in column \a col.
+
+ \sa Q3ListViewItem::setRenameEnabled()
+*/
+
+/*!
+ Constructs a new empty list view called \a name with parent \a
+ parent and widget attributes \a f.
+
+ This constructor sets the \c WA_StaticContent and the \c
+ Qt::WA_NoBackground attributes to boost performance when drawing
+ Q3ListViewItems. This may be unsuitable for custom Q3ListViewItem
+ classes, in which case Qt::WA_StaticContents and Qt::WA_NoBackground
+ should be cleared on the viewport() after construction.
+
+ \sa QWidget::setAttribute()
+*/
+Q3ListView::Q3ListView(QWidget * parent, const char *name, Qt::WindowFlags f)
+ : Q3ScrollView(parent, name, f | Qt::WStaticContents | Qt::WNoAutoErase)
+{
+ init();
+}
+
+void Q3ListView::init()
+{
+ d = new Q3ListViewPrivate;
+ d->vci = 0;
+ d->timer = new QTimer(this);
+ d->levelWidth = 20;
+ d->r = 0;
+ d->rootIsExpandable = 0;
+ d->h = new Q3Header(this, "list view header");
+ d->h->installEventFilter(this);
+ d->focusItem = 0;
+ d->oldFocusItem = 0;
+ d->dirtyItemTimer = new QTimer(this);
+ d->visibleTimer = new QTimer(this);
+ d->renameTimer = new QTimer(this);
+ d->autoopenTimer = new QTimer(this);
+ d->margin = 1;
+ d->selectionMode = Q3ListView::Single;
+ d->sortcolumn = 0;
+ d->ascending = true;
+ d->allColumnsShowFocus = false;
+ d->fontMetricsHeight = fontMetrics().height();
+ d->h->setTracking(true);
+ d->buttonDown = false;
+ d->ignoreDoubleClick = false;
+ d->scrollTimer = 0;
+ d->sortIndicator = false;
+ d->clearing = false;
+ d->minLeftBearing = fontMetrics().minLeftBearing();
+ d->minRightBearing = fontMetrics().minRightBearing();
+ d->ellipsisWidth = fontMetrics().width(QLatin1String("...")) * 2;
+ d->highlighted = 0;
+ d->pressedItem = 0;
+ d->selectAnchor = 0;
+ d->select = true;
+ d->startDragItem = 0;
+ d->toolTips = true;
+ d->updateHeader = false;
+ d->fullRepaintOnComlumnChange = false;
+ d->resizeMode = NoColumn;
+ d->defRenameAction = Reject;
+ d->pressedEmptyArea = false;
+ d->startEdit = true;
+ d->ignoreEditAfterFocus = false;
+ d->inMenuMode = false;
+ d->pressedSelected = false;
+
+ setMouseTracking(true);
+ viewport()->setMouseTracking(true);
+
+ connect(d->timer, SIGNAL(timeout()),
+ this, SLOT(updateContents()));
+ connect(d->dirtyItemTimer, SIGNAL(timeout()),
+ this, SLOT(updateDirtyItems()));
+ connect(d->visibleTimer, SIGNAL(timeout()),
+ this, SLOT(makeVisible()));
+ connect(d->renameTimer, SIGNAL(timeout()),
+ this, SLOT(startRename()));
+ connect(d->autoopenTimer, SIGNAL(timeout()),
+ this, SLOT(openFocusItem()));
+
+ connect(d->h, SIGNAL(sizeChange(int,int,int)),
+ this, SLOT(handleSizeChange(int,int,int)));
+ connect(d->h, SIGNAL(indexChange(int,int,int)),
+ this, SLOT(handleIndexChange()));
+ connect(d->h, SIGNAL(sectionClicked(int)),
+ this, SLOT(changeSortColumn(int)));
+ connect(d->h, SIGNAL(sectionHandleDoubleClicked(int)),
+ this, SLOT(adjustColumn(int)));
+ connect(horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ d->h, SLOT(setOffset(int)));
+ connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ d->h, SLOT(setOffset(int)));
+
+ // will access d->r
+ Q3ListViewPrivate::Root * r = new Q3ListViewPrivate::Root(this);
+ r->is_root = true;
+ d->r = r;
+ d->r->setSelectable(false);
+
+ viewport()->setFocusProxy(this);
+ viewport()->setFocusPolicy(Qt::WheelFocus);
+ setFocusPolicy(Qt::WheelFocus);
+ viewport()->setBackgroundRole(QPalette::Base);
+ setAttribute(Qt::WA_MacShowFocusRect);
+}
+
+/*!
+ \property Q3ListView::showSortIndicator
+ \brief whether the list view header should display a sort indicator.
+
+ If this property is true, an arrow is drawn in the header of the
+ list view to indicate the sort order of the list view contents.
+ The arrow will be drawn in the correct column and will point up or
+ down, depending on the current sort direction. The default is
+ false (don't show an indicator).
+
+ \sa Q3Header::setSortIndicator()
+*/
+
+void Q3ListView::setShowSortIndicator(bool show)
+{
+ if (show == d->sortIndicator)
+ return;
+
+ d->sortIndicator = show;
+ if (d->sortcolumn != Unsorted && d->sortIndicator)
+ d->h->setSortIndicator(d->sortcolumn, d->ascending);
+ else
+ d->h->setSortIndicator(-1);
+}
+
+bool Q3ListView::showSortIndicator() const
+{
+ return d->sortIndicator;
+}
+
+/*!
+ \property Q3ListView::showToolTips
+ \brief whether this list view should show tooltips for truncated column texts
+
+ The default is true.
+*/
+
+void Q3ListView::setShowToolTips(bool b)
+{
+ d->toolTips = b;
+}
+
+bool Q3ListView::showToolTips() const
+{
+ return d->toolTips;
+}
+
+/*!
+ \property Q3ListView::resizeMode
+ \brief whether all, none or the only the last column should be resized
+
+ Specifies whether all, none or only the last column should be
+ resized to fit the full width of the list view. The values for this
+ property can be one of: \c NoColumn (the default), \c AllColumns
+ or \c LastColumn.
+
+ \warning Setting the resize mode should be done after all necessary
+ columns have been added to the list view, otherwise the behavior is
+ undefined.
+
+ \sa Q3Header, header()
+*/
+
+void Q3ListView::setResizeMode(ResizeMode m)
+{
+ d->resizeMode = m;
+ if (m == NoColumn)
+ header()->setStretchEnabled(false);
+ else if (m == AllColumns)
+ header()->setStretchEnabled(true);
+ else
+ header()->setStretchEnabled(true, header()->count() - 1);
+}
+
+Q3ListView::ResizeMode Q3ListView::resizeMode() const
+{
+ return d->resizeMode;
+}
+
+/*!
+ Destroys the list view, deleting all its items, and frees up all
+ allocated resources.
+*/
+
+Q3ListView::~Q3ListView()
+{
+ for (int j = 0; j < d->iterators.size(); ++j) {
+ Q3ListViewItemIterator *i = d->iterators.at(j);
+ i->listView = 0;
+ }
+
+ d->focusItem = 0;
+ delete d->r;
+ d->r = 0;
+ delete d->vci;
+ d->vci = 0;
+#if 0
+ delete d->toolTip;
+ d->toolTip = 0;
+#endif
+ delete d;
+ d = 0;
+}
+
+
+/*!
+ Calls Q3ListViewItem::paintCell() and
+ Q3ListViewItem::paintBranches() as necessary for all list view
+ items that require repainting in the \a cw pixels wide and \a ch
+ pixels high bounding rectangle starting at position \a cx, \a cy
+ with offset \a ox, \a oy. Uses the painter \a p.
+*/
+
+void Q3ListView::drawContentsOffset(QPainter * p, int ox, int oy,
+ int cx, int cy, int cw, int ch)
+{
+ if (columns() == 0) {
+ paintEmptyArea(p, QRect(cx, cy, cw, ch));
+ return;
+ }
+
+ if (d->drawables.isEmpty() ||
+ d->topPixel > cy ||
+ d->bottomPixel < cy + ch - 1 ||
+ d->r->maybeTotalHeight < 0)
+ buildDrawableList();
+
+ if (!d->dirtyItems.isEmpty()) {
+ QRect br(cx - ox, cy - oy, cw, ch);
+ for (int i = 0; i < d->dirtyItems.size(); ++i) {
+ const Q3ListViewItem * item = d->dirtyItems.at(i);
+ QRect ir = itemRect(item).intersected(viewport()->visibleRect());
+ if (ir.isEmpty() || br.contains(ir))
+ // we're painting this one, or it needs no painting: forget it
+ d->dirtyItems.removeAt(i);
+ }
+ if (d->dirtyItems.count()) {
+ // there are still items left that need repainting
+ d->dirtyItemTimer->start(0, true);
+ } else {
+ // we're painting all items that need to be painted
+ d->dirtyItems.clear();
+ d->dirtyItemTimer->stop();
+ }
+ }
+
+ p->setFont(font());
+
+ QRect r;
+ int fx = -1, x, fc = 0, lc = 0;
+ int tx = -1;
+
+ for (int i = 0; i < d->drawables.size(); ++i) {
+ Q3ListViewPrivate::DrawableItem current = d->drawables.at(i);
+ if (!current.i->isVisible())
+ continue;
+ int ih = current.i->height();
+ int ith = current.i->totalHeight();
+ int c;
+ int cs;
+
+ // need to paint current?
+ if (ih > 0 && current.y < cy+ch && current.y+ih > cy) {
+ if (fx < 0) {
+ // find first interesting column, once
+ x = 0;
+ c = 0;
+ cs = d->h->cellSize(0);
+ while (x + cs <= cx && c < d->h->count()) {
+ x += cs;
+ c++;
+ if (c < d->h->count())
+ cs = d->h->cellSize(c);
+ }
+ fx = x;
+ fc = c;
+ while(x < cx + cw && c < d->h->count()) {
+ x += cs;
+ c++;
+ if (c < d->h->count())
+ cs = d->h->cellSize(c);
+ }
+ lc = c;
+ }
+
+ x = fx;
+ c = fc;
+ // draw to last interesting column
+
+ bool drawActiveSelection = hasFocus() || d->inMenuMode ||
+ !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)
+ || (currentItem() && currentItem()->renameBox
+ && currentItem()->renameBox->hasFocus());
+ QPalette pal = palette();
+ if(!drawActiveSelection)
+ pal.setCurrentColorGroup(QPalette::Inactive);
+
+ while (c < lc) {
+ int i = d->h->mapToLogical(c);
+ cs = d->h->cellSize(c);
+ r.setRect(x - ox, current.y - oy, cs, ih);
+ if (i == 0 && current.i->parentItem)
+ r.setLeft(r.left() + current.l * treeStepSize());
+
+ p->save();
+ // No need to paint if the cell isn't technically visible
+ if (!(r.width() == 0 || r.height() == 0)) {
+ p->translate(r.left(), r.top());
+ int ac = d->h->mapToLogical(c);
+ // map to Left currently. This should change once we
+ // can really reverse the listview.
+ int align = columnAlignment(ac);
+ if (align == Qt::AlignAuto) align = Qt::AlignLeft;
+ current.i->paintCell(p, pal, ac, r.width(), align);
+ }
+ p->restore();
+ x += cs;
+ c++;
+ }
+
+ if (current.i == d->focusItem && hasFocus() &&
+ !d->allColumnsShowFocus) {
+ p->save();
+ int cell = d->h->mapToActual(0);
+ QRect r(d->h->cellPos(cell) - ox, current.y - oy, d->h->cellSize(cell), ih);
+ if (current.i->parentItem)
+ r.setLeft(r.left() + current.l * treeStepSize());
+ if (r.left() < r.right())
+ current.i->paintFocus(p, palette(), r);
+ p->restore();
+ }
+ }
+
+ const int cell = d->h->mapToActual(0);
+
+ // does current need focus indication?
+ if (current.i == d->focusItem && hasFocus() &&
+ d->allColumnsShowFocus) {
+ p->save();
+ int x = -contentsX();
+ int w = header()->cellPos(header()->count() - 1) +
+ header()->cellSize(header()->count() - 1);
+
+ r.setRect(x, current.y - oy, w, ih);
+ if (d->h->mapToActual(0) == 0 || (current.l == 0 && !rootIsDecorated())) {
+ int offsetx = qMin(current.l * treeStepSize(), d->h->cellSize(cell));
+ r.setLeft(r.left() + offsetx);
+ current.i->paintFocus(p, palette(), r);
+ } else {
+ int xdepth = qMin(treeStepSize() * (current.i->depth() + (rootIsDecorated() ? 1 : 0))
+ + itemMargin(), d->h->cellSize(cell));
+ xdepth += d->h->cellPos(cell);
+ QRect r1(r);
+ r1.setRight(d->h->cellPos(cell) - 1);
+ QRect r2(r);
+ r2.setLeft(xdepth - 1);
+ current.i->paintFocus(p, palette(), r1);
+ current.i->paintFocus(p, palette(), r2);
+ }
+ p->restore();
+ }
+
+ if (tx < 0)
+ tx = d->h->cellPos(cell);
+
+ // do any children of current need to be painted?
+ if (ih != ith &&
+ (current.i != d->r || d->rootIsExpandable) &&
+ current.y + ith > cy &&
+ current.y + ih < cy + ch &&
+ tx + current.l * treeStepSize() < cx + cw &&
+ tx + (current.l+1) * treeStepSize() > cx) {
+ // compute the clip rectangle the safe way
+
+ int rtop = current.y + ih;
+ int rbottom = current.y + ith;
+ int rleft = tx + current.l*treeStepSize();
+ int rright = rleft + treeStepSize();
+
+ int crtop = qMax(rtop, cy);
+ int crbottom = qMin(rbottom, cy+ch);
+ int crleft = qMax(rleft, cx);
+ int crright = qMin(rright, cx+cw);
+
+ r.setRect(crleft-ox, crtop-oy,
+ crright-crleft, crbottom-crtop);
+
+ if (r.isValid()) {
+ p->save();
+ p->setClipRect(QRect(d->h->cellPos(cell), 0, d->h->cellSize(cell), height()));
+ p->translate(rleft-ox, crtop-oy);
+
+ current.i->paintBranches(p, palette(), treeStepSize(),
+ rtop - crtop, r.height());
+ p->restore();
+ }
+ }
+ }
+
+ if (d->r->totalHeight() < cy + ch)
+ paintEmptyArea(p, QRect(cx - ox, d->r->totalHeight() - oy,
+ cw, cy + ch - d->r->totalHeight()));
+
+ int c = d->h->count()-1;
+ if (c >= 0 &&
+ d->h->cellPos(c) + d->h->cellSize(c) < cx + cw) {
+ c = d->h->cellPos(c) + d->h->cellSize(c);
+ paintEmptyArea(p, QRect(c - ox, cy - oy, cx + cw - c, ch));
+ }
+}
+
+
+
+/*!
+ Paints \a rect so that it looks like empty background using
+ painter \a p. \a rect is in widget coordinates, ready to be fed to
+ \a p.
+
+ The default function fills \a rect with the
+ viewport()->backgroundBrush().
+*/
+
+void Q3ListView::paintEmptyArea(QPainter * p, const QRect & rect)
+{
+ QStyleOptionQ3ListView opt = getStyleOption(this, 0);
+ opt.rect = rect;
+ opt.sortColumn = d->sortcolumn;
+ opt.subControls = QStyle::SC_Q3ListView;
+ style()->drawComplexControl(QStyle::CC_Q3ListView, &opt, p, this);
+}
+
+
+/*
+ Rebuilds the list of drawable Q3ListViewItems. This function is
+ const so that const functions can call it without requiring
+ d->drawables to be mutable.
+*/
+
+void Q3ListView::buildDrawableList() const
+{
+ d->r->enforceSortOrder();
+
+ QStack<Q3ListViewPrivate::DrawableItem> stack;
+ Q3ListViewPrivate::DrawableItem di(((int)d->rootIsExpandable)-1, 0, d->r);
+ stack.push(di);
+
+ Q3ListView *that = const_cast<Q3ListView *>(this);
+
+ // could mess with cy and ch in order to speed up vertical
+ // scrolling
+ int cy = contentsY();
+ int ch = that->visibleHeight();
+ d->topPixel = cy + ch; // one below bottom
+ d->bottomPixel = cy - 1; // one above top
+
+ that->d->drawables.clear();
+
+ while (!stack.isEmpty()) {
+ Q3ListViewPrivate::DrawableItem cur = stack.pop();
+
+ int ih = cur.i->height();
+ int ith = cur.i->totalHeight();
+
+ // is this item, or its branch symbol, inside the viewport?
+ if (cur.y + ith >= cy && cur.y < cy + ch) {
+ that->d->drawables.append(cur);
+ // perhaps adjust topPixel up to this item? may be adjusted
+ // down again if any children are not to be painted
+ if (cur.y < d->topPixel)
+ d->topPixel = cur.y;
+ // bottompixel is easy: the bottom item drawn contains it
+ d->bottomPixel = cur.y + ih - 1;
+ }
+
+ // push younger sibling of cur on the stack?
+ if (cur.y + ith < cy+ch && cur.i->siblingItem)
+ stack.push(Q3ListViewPrivate::DrawableItem(cur.l, cur.y + ith, cur.i->siblingItem));
+
+ // do any children of cur need to be painted?
+ if (cur.i->isOpen() && cur.i->childCount() &&
+ cur.y + ith > cy &&
+ cur.y + ih < cy + ch) {
+ cur.i->enforceSortOrder();
+
+ Q3ListViewItem * c = cur.i->childItem;
+ int y = cur.y + ih;
+
+ // if any of the children are not to be painted, skip them
+ // and invalidate topPixel
+ while (c && y + c->totalHeight() <= cy) {
+ y += c->totalHeight();
+ c = c->siblingItem;
+ d->topPixel = cy + ch;
+ }
+
+ // push one child on the stack, if there is at least one
+ // needing to be painted
+ if (c && y < cy+ch)
+ stack.push(Q3ListViewPrivate::DrawableItem(cur.l + 1, y, c));
+ }
+ }
+}
+
+/*!
+ \property Q3ListView::treeStepSize
+ \brief the number of pixels a child is offset from its parent
+
+ The default is 20 pixels.
+
+ Of course, this property is only meaningful for hierarchical list
+ views.
+*/
+
+int Q3ListView::treeStepSize() const
+{
+ return d->levelWidth;
+}
+
+void Q3ListView::setTreeStepSize(int size)
+{
+ if (size != d->levelWidth) {
+ d->levelWidth = size;
+ viewport()->repaint();
+ }
+}
+
+/*!
+ Inserts item \a i into the list view as a top-level item. You do
+ not need to call this unless you've called takeItem(\a i) or
+ Q3ListViewItem::takeItem(\a i) and need to reinsert \a i elsewhere.
+
+ \sa Q3ListViewItem::takeItem() takeItem()
+*/
+
+void Q3ListView::insertItem(Q3ListViewItem * i)
+{
+ if (d->r) // not for d->r itself
+ d->r->insertItem(i);
+}
+
+
+/*!
+ Removes and deletes all the items in this list view and triggers
+ an update.
+
+ \sa triggerUpdate()
+*/
+
+void Q3ListView::clear()
+{
+ bool wasUpdatesEnabled = viewport()->updatesEnabled();
+ if (wasUpdatesEnabled)
+ viewport()->setUpdatesEnabled(false);
+ setContentsPos(0, 0);
+ if (wasUpdatesEnabled)
+ viewport()->setUpdatesEnabled(true);
+
+ bool block = signalsBlocked();
+ blockSignals(true);
+ d->clearing = true;
+ clearSelection();
+ for (int j = 0; j < d->iterators.size(); ++j) {
+ Q3ListViewItemIterator *i = d->iterators.at(j);
+ i->curr = 0;
+ }
+
+ d->drawables.clear();
+ d->dirtyItems.clear();
+ d->dirtyItemTimer->stop();
+
+ d->highlighted = 0;
+ d->focusItem = 0;
+ d->selectAnchor = 0;
+ d->pressedItem = 0;
+ d->startDragItem = 0;
+
+ // if it's down its downness makes no sense, so undown it
+ d->buttonDown = false;
+
+ Q3ListViewItem *c = (Q3ListViewItem *)d->r->firstChild();
+ Q3ListViewItem *n;
+ while(c) {
+ n = (Q3ListViewItem *)c->nextSibling();
+ delete c;
+ c = n;
+ }
+ resizeContents(d->h->sizeHint().width(), contentsHeight());
+ delete d->r;
+ d->r = 0;
+ Q3ListViewPrivate::Root * r = new Q3ListViewPrivate::Root(this);
+ r->is_root = true;
+ d->r = r;
+ d->r->setSelectable(false);
+ blockSignals(block);
+ triggerUpdate();
+ d->clearing = false;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3ListView::setContentsPos(int x, int y)
+{
+ updateGeometries();
+ Q3ScrollView::setContentsPos(x, y);
+}
+
+/*!
+ Adds a \a width pixels wide column with the column header \a label
+ to the list view, and returns the index of the new column.
+
+ All columns apart from the first one are inserted to the right of
+ the existing ones.
+
+ If \a width is negative, the new column's \l WidthMode is set to
+ \c Maximum instead of \c Manual.
+
+ \sa setColumnText() setColumnWidth() setColumnWidthMode()
+*/
+int Q3ListView::addColumn(const QString &label, int width)
+{
+ int c = d->h->addLabel(label, width);
+ d->column.resize(c+1);
+ d->column[c].wmode = (width >= 0 ? Manual : Maximum);
+ updateGeometries();
+ updateGeometry();
+ return c;
+}
+
+/*!
+ \overload
+
+ Adds a \a width pixels wide new column with the header \a label
+ and the \a icon to the list view, and returns the index of the
+ column.
+
+ If \a width is negative, the new column's \l WidthMode is set to
+ \c Maximum, and to \c Manual otherwise.
+
+ \sa setColumnText() setColumnWidth() setColumnWidthMode()
+*/
+int Q3ListView::addColumn(const QIcon& icon, const QString &label, int width)
+{
+ int c = d->h->addLabel(icon, label, width);
+ d->column.resize(c+1);
+ d->column[c].wmode = (width >= 0 ? Manual : Maximum);
+ updateGeometries();
+ updateGeometry();
+ return c;
+}
+
+/*!
+ \property Q3ListView::columns
+ \brief the number of columns in this list view
+
+ \sa addColumn(), removeColumn()
+*/
+
+int Q3ListView::columns() const
+{
+ return d->column.count();
+}
+
+/*!
+ Removes the column at position \a index.
+*/
+
+void Q3ListView::removeColumn(int index)
+{
+ if (index < 0 || index > (int)d->column.count() - 1)
+ return;
+
+ if (d->vci) {
+ Q3ListViewPrivate::ViewColumnInfo *vi = d->vci, *prev = 0, *next = 0;
+ for (int i = 0; i < index; ++i) {
+ if (vi) {
+ prev = vi;
+ vi = vi->next;
+ }
+ }
+ if (vi) {
+ next = vi->next;
+ if (prev)
+ prev->next = next;
+ vi->next = 0;
+ delete vi;
+ if (index == 0)
+ d->vci = next;
+ }
+ }
+
+ Q3ListViewItemIterator it(this);
+ for (; it.current(); ++it) {
+ Q3ListViewPrivate::ItemColumnInfo *ci = (Q3ListViewPrivate::ItemColumnInfo*)it.current()->columns;
+ if (ci) {
+ Q3ListViewPrivate::ItemColumnInfo *prev = 0, *next = 0;
+ for (int i = 0; i < index; ++i) {
+ if (ci) {
+ prev = ci;
+ ci = ci->next;
+ }
+ }
+ if (ci) {
+ next = ci->next;
+ if (prev)
+ prev->next = next;
+ ci->next = 0;
+ delete ci;
+ if (index == 0)
+ it.current()->columns = next;
+ }
+ }
+ }
+
+ for (int i = index; i < (int)d->column.size() - 1; ++i)
+ d->column[i] = d->column[i + 1];
+ d->column.resize(d->column.size() - 1);
+
+ d->h->removeLabel(index);
+ if (d->resizeMode == LastColumn)
+ d->h->setStretchEnabled(true, d->h->count() - 1);
+
+ updateGeometries();
+ if (d->column.count() == 0)
+ clear();
+ updateGeometry();
+ viewport()->update();
+}
+
+/*!
+ Sets the heading of column \a column to \a label.
+
+ \sa columnText()
+*/
+void Q3ListView::setColumnText(int column, const QString &label)
+{
+ if (column < d->h->count()) {
+ d->h->setLabel(column, label);
+ updateGeometries();
+ updateGeometry();
+ }
+}
+
+/*!
+ \overload
+
+ Sets the heading of column \a column to \a icon and \a label.
+
+ \sa columnText()
+*/
+void Q3ListView::setColumnText(int column, const QIcon& icon, const QString &label)
+{
+ if (column < d->h->count()) {
+ d->h->setLabel(column, icon, label);
+ updateGeometries();
+ }
+}
+
+/*!
+ Sets the width of column \a column to \a w pixels. Note that if
+ the column has a \c WidthMode other than \c Manual, this width
+ setting may be subsequently overridden.
+
+ \sa columnWidth()
+*/
+void Q3ListView::setColumnWidth(int column, int w)
+{
+ int oldw = d->h->sectionSize(column);
+ if (column < d->h->count() && oldw != w) {
+ d->h->resizeSection(column, w);
+ disconnect(d->h, SIGNAL(sizeChange(int,int,int)),
+ this, SLOT(handleSizeChange(int,int,int)));
+ emit d->h->sizeChange(column, oldw, w);
+ connect(d->h, SIGNAL(sizeChange(int,int,int)),
+ this, SLOT(handleSizeChange(int,int,int)));
+ viewport()->update();
+ }
+}
+
+
+/*!
+ Returns the text of column \a c.
+
+ \sa setColumnText()
+*/
+
+QString Q3ListView::columnText(int c) const
+{
+ return d->h->label(c);
+}
+
+/*!
+ Returns the width of column \a c.
+
+ \sa setColumnWidth()
+*/
+
+int Q3ListView::columnWidth(int c) const
+{
+ int actual = d->h->mapToActual(c);
+ return d->h->cellSize(actual);
+}
+
+
+/*!
+ \enum Q3ListView::WidthMode
+
+ This enum type describes how the width of a column in the view
+ changes.
+
+ \value Manual the column width does not change automatically.
+
+ \value Maximum the column is automatically sized according to the
+ widths of all items in the column. (Note: The column never shrinks
+ in this case.) This means that the column is always resized to the
+ width of the item with the largest width in the column.
+
+ \sa setColumnWidth() setColumnWidthMode() columnWidth()
+*/
+
+
+/*!
+ Sets column \a{c}'s width mode to \a mode. The default depends on
+ the original width argument to addColumn().
+
+ \sa Q3ListViewItem::width()
+*/
+
+void Q3ListView::setColumnWidthMode(int c, WidthMode mode)
+{
+ if (c >= 0 && c < d->h->count())
+ d->column[c].wmode = mode;
+}
+
+
+/*!
+ Returns the \c WidthMode for column \a c.
+
+ \sa setColumnWidthMode()
+*/
+
+Q3ListView::WidthMode Q3ListView::columnWidthMode(int c) const
+{
+ if (c >= 0 && c < d->h->count())
+ return d->column[c].wmode;
+ else
+ return Manual;
+}
+
+
+/*!
+ Sets column \a{column}'s alignment to \a align. The alignment is
+ ultimately passed to Q3ListViewItem::paintCell() for each item in
+ the list view. For horizontally aligned text with Qt::AlignLeft or
+ Qt::AlignHCenter the ellipsis (...) will be to the right, for
+ Qt::AlignRight the ellipsis will be to the left.
+
+ \sa Qt::Alignment
+*/
+
+void Q3ListView::setColumnAlignment(int column, int align)
+{
+ if (column < 0)
+ return;
+ if (!d->vci)
+ d->vci = new Q3ListViewPrivate::ViewColumnInfo;
+ Q3ListViewPrivate::ViewColumnInfo * l = d->vci;
+ while(column) {
+ if (!l->next)
+ l->next = new Q3ListViewPrivate::ViewColumnInfo;
+ l = l->next;
+ column--;
+ }
+ if (l->align == align)
+ return;
+ l->align = align;
+ triggerUpdate();
+}
+
+
+/*!
+ Returns the alignment of column \a column. The default is \c
+ Qt::AlignAuto.
+
+ \sa Qt::Alignment
+*/
+
+int Q3ListView::columnAlignment(int column) const
+{
+ if (column < 0 || !d->vci)
+ return Qt::AlignAuto;
+ Q3ListViewPrivate::ViewColumnInfo * l = d->vci;
+ while(column) {
+ if (!l->next)
+ l->next = new Q3ListViewPrivate::ViewColumnInfo;
+ l = l->next;
+ column--;
+ }
+ return l ? l->align : Qt::AlignAuto;
+}
+
+
+
+/*!
+ \internal
+*/
+void Q3ListView::show()
+{
+ // Reimplemented to setx the correct background mode and viewed
+ // area size.
+ if (!isVisible()) {
+ reconfigureItems();
+ updateGeometries();
+ }
+ Q3ScrollView::show();
+}
+
+
+/*!
+ Updates the sizes of the viewport, header, scroll bars and so on.
+
+ \warning Don't call this directly; call triggerUpdate() instead.
+*/
+
+void Q3ListView::updateContents()
+{
+ if (d->updateHeader)
+ header()->adjustHeaderSize();
+ d->updateHeader = false;
+ if (!isVisible()) {
+ // Not in response to a setText/setPixmap any more.
+ return;
+ }
+ d->drawables.clear();
+ viewport()->setUpdatesEnabled(false);
+ updateGeometries();
+ viewport()->setUpdatesEnabled(true);
+ viewport()->repaint();
+}
+
+
+void Q3ListView::updateGeometries()
+{
+ int th = d->r->totalHeight();
+ int tw = d->h->headerWidth();
+ if (d->h->offset() &&
+ tw < d->h->offset() + d->h->width())
+ horizontalScrollBar()->setValue(tw - Q3ListView::d->h->width());
+#if 0
+ if (QApplication::reverseLayout() && d->h->offset() != horizontalScrollBar()->value())
+ horizontalScrollBar()->setValue(d->h->offset());
+#endif
+ verticalScrollBar()->raise();
+ resizeContents(tw, th);
+ d->drawables.clear();
+ if (d->h->isHidden()) {
+ setMargins(0, 0, 0, 0);
+ } else {
+ QSize hs(d->h->sizeHint());
+ setMargins(0, hs.height(), 0, 0);
+ d->h->setGeometry(viewport()->x(), viewport()->y()-hs.height(),
+ visibleWidth(), hs.height());
+ }
+}
+
+
+/*!
+ Updates the display when the section \a section has changed size
+ from the old size, \a os, to the new size, \a ns.
+*/
+
+void Q3ListView::handleSizeChange(int section, int os, int ns)
+{
+ bool upe = viewport()->updatesEnabled();
+ if (upe)
+ viewport()->setUpdatesEnabled(false);
+ viewport()->setAttribute(Qt::WA_UpdatesDisabled, true);
+ int sx = horizontalScrollBar()->value();
+ bool sv = horizontalScrollBar()->isVisible();
+ updateGeometries();
+ bool fullRepaint = d->fullRepaintOnComlumnChange || sx != horizontalScrollBar()->value()
+ || sv != horizontalScrollBar()->isVisible();
+ d->fullRepaintOnComlumnChange = false;
+ if (upe)
+ viewport()->setUpdatesEnabled(true);
+
+ if (fullRepaint) {
+ viewport()->repaint();
+ return;
+ }
+
+ int actual = d->h->mapToActual(section);
+ int dx = ns - os;
+ int left = d->h->cellPos(actual) - contentsX() + d->h->cellSize(actual);
+ if (dx > 0)
+ left -= dx;
+ if (left < visibleWidth())
+ viewport()->scroll(dx, 0, QRect(left, 0, visibleWidth() - left, visibleHeight()));
+ viewport()->repaint(left - 4 - d->ellipsisWidth, 0, 4 + d->ellipsisWidth,
+ visibleHeight()); // border between the items and ellipses width
+
+ // map auto to left for now. Need to fix this once we support
+ // reverse layout on the listview.
+ int align = columnAlignment(section);
+ if (align == Qt::AlignAuto) align = Qt::AlignLeft;
+ if (align != Qt::AlignAuto && align != Qt::AlignLeft)
+ viewport()->repaint(d->h->cellPos(actual) - contentsX(), 0,
+ d->h->cellSize(actual), visibleHeight());
+
+ if (currentItem() && currentItem()->renameBox) {
+ QRect r = itemRect(currentItem());
+ r = QRect(viewportToContents(r.topLeft()), r.size());
+ r.setLeft(header()->sectionPos(currentItem()->renameCol));
+ r.setWidth(header()->sectionSize(currentItem()->renameCol) - 1);
+ if (currentItem()->renameCol == 0)
+ r.setLeft(r.left() + itemMargin() + (currentItem()->depth() +
+ (rootIsDecorated() ? 1 : 0)) * treeStepSize() - 1);
+ if (currentItem()->pixmap(currentItem()->renameCol))
+ r.setLeft(r.left() + currentItem()->pixmap(currentItem()->renameCol)->width());
+ if (r.x() - contentsX() < 0)
+ r.setX(contentsX());
+ if (r.width() > visibleWidth())
+ r.setWidth(visibleWidth());
+ addChild(currentItem()->renameBox, r.x(), r.y());
+ currentItem()->renameBox->resize(r.size());
+ }
+}
+
+
+/*
+ Very smart internal slot that repaints \e only the items that need
+ to be repainted. Don't use this directly; call repaintItem()
+ instead.
+*/
+
+void Q3ListView::updateDirtyItems()
+{
+ if (d->timer->isActive() || d->dirtyItems.isEmpty())
+ return;
+ QRect ir;
+ for (int i = 0; i < d->dirtyItems.size(); ++i) {
+ const Q3ListViewItem *item = d->dirtyItems.at(i);
+ ir = ir.united(itemRect(item));
+ }
+ d->dirtyItems.clear();
+ if (!ir.isEmpty()) { // rectangle to be repainted
+ if (ir.x() < 0)
+ ir.moveBy(-ir.x(), 0);
+ viewport()->repaint(ir);
+ }
+}
+
+
+void Q3ListView::makeVisible()
+{
+ if (d->focusItem)
+ ensureItemVisible(d->focusItem);
+}
+
+
+/*!
+ Ensures that the header is correctly sized and positioned when the
+ resize event \a e occurs.
+*/
+
+void Q3ListView::resizeEvent(QResizeEvent *e)
+{
+ Q3ScrollView::resizeEvent(e);
+ d->fullRepaintOnComlumnChange = true;
+ d->h->resize(visibleWidth(), d->h->height());
+ d->h->adjustHeaderSize();
+}
+
+/*! \reimp */
+
+void Q3ListView::viewportResizeEvent(QResizeEvent *e)
+{
+ Q3ScrollView::viewportResizeEvent(e);
+ d->h->resize(visibleWidth(), d->h->height());
+ if (resizeMode() != NoColumn && currentItem() && currentItem()->renameBox) {
+ QRect r = itemRect(currentItem());
+ r = QRect(viewportToContents(r.topLeft()), r.size());
+ r.setLeft(header()->sectionPos(currentItem()->renameCol));
+ r.setWidth(header()->sectionSize(currentItem()->renameCol) - 1);
+ if (currentItem()->renameCol == 0)
+ r.setLeft(r.left() + itemMargin() + (currentItem()->depth() +
+ (rootIsDecorated() ? 1 : 0)) * treeStepSize() - 1);
+ if (currentItem()->pixmap(currentItem()->renameCol))
+ r.setLeft(r.left() + currentItem()->pixmap(currentItem()->renameCol)->width());
+ if (r.x() - contentsX() < 0)
+ r.setX(contentsX());
+ if (r.width() > visibleWidth())
+ r.setWidth(visibleWidth());
+ addChild(currentItem()->renameBox, r.x(), r.y());
+ currentItem()->renameBox->resize(r.size());
+ }
+}
+
+/*!
+ Triggers a size, geometry and content update during the next
+ iteration of the event loop. Ensures that there'll be just one
+ update to avoid flicker.
+*/
+
+void Q3ListView::triggerUpdate()
+{
+ if (!isVisible() || !updatesEnabled()) {
+ // Not in response to a setText/setPixmap any more.
+ return; // it will update when shown, or something.
+ }
+
+ d->timer->start(0, true);
+}
+
+
+/*!
+ Redirects the event \a e relating to object \a o, for the viewport
+ to mousePressEvent(), keyPressEvent() and friends.
+*/
+
+bool Q3ListView::eventFilter(QObject * o, QEvent * e)
+{
+ if (o == d->h &&
+ e->type() >= QEvent::MouseButtonPress &&
+ e->type() <= QEvent::MouseMove) {
+ QMouseEvent * me = (QMouseEvent *)e;
+ QMouseEvent me2(me->type(),
+ QPoint(me->pos().x(),
+ me->pos().y() - d->h->height()),
+ me->button(), me->state());
+ switch(me2.type()) {
+ case QEvent::MouseButtonDblClick:
+ if (me2.button() == Qt::RightButton)
+ return true;
+ break;
+ case QEvent::MouseMove:
+ if (me2.state() & Qt::RightButton) {
+ viewportMouseMoveEvent(&me2);
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ } else if (o == viewport()) {
+ QFocusEvent * fe = (QFocusEvent *)e;
+
+ switch(e->type()) {
+ case QEvent::FocusIn:
+ focusInEvent(fe);
+ return true;
+ case QEvent::FocusOut:
+ focusOutEvent(fe);
+ return true;
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip:
+ {
+ if (!showToolTips())
+ return false;
+
+ QHelpEvent *he = static_cast<QHelpEvent *>(e);
+ Q3ListViewItem *item = itemAt(he->pos());
+ QPoint contentsPos = viewportToContents(he->pos());
+ if (!item || !item->columns) {
+ QToolTip::showText(he->globalPos(), QString(), viewport());
+ return true;
+ }
+ int col = header()->sectionAt(contentsPos.x());
+ Q3ListViewPrivate::ItemColumnInfo *ci = (Q3ListViewPrivate::ItemColumnInfo*)item->columns;
+ for (int i = 0; ci && (i < col); ++i)
+ ci = ci->next;
+
+ if (!ci || !ci->truncated)
+ QToolTip::showText(he->globalPos(), QString(), viewport());
+ else
+ QToolTip::showText(he->globalPos(), item->text(col), viewport());
+ return true;
+ }
+#endif
+ default:
+ // nothing
+ break;
+ }
+ } else if (qobject_cast<QLineEdit*>(o)) {
+ if (currentItem() && currentItem()->renameBox) {
+ if (e->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if (ke->key() == Qt::Key_Return ||
+ ke->key() == Qt::Key_Enter) {
+ currentItem()->okRename(currentItem()->renameCol);
+ return true;
+ } else if (ke->key() == Qt::Key_Escape) {
+ currentItem()->cancelRename(currentItem()->renameCol);
+ return true;
+ }
+ } else if (e->type() == QEvent::FocusOut) {
+ if (((QFocusEvent*)e)->reason() != Qt::PopupFocusReason) {
+ QCustomEvent *e = new QCustomEvent(9999);
+ QApplication::postEvent(o, e);
+ return true;
+ }
+ } else if (e->type() == 9999) {
+ if (d->defRenameAction == Reject)
+ currentItem()->cancelRename(currentItem()->renameCol);
+ else
+ currentItem()->okRename(currentItem()->renameCol);
+ return true;
+ }
+ }
+ }
+
+ return Q3ScrollView::eventFilter(o, e);
+}
+
+
+/*!
+ Returns a pointer to the list view containing this item.
+
+ Note that this function traverses the items to the root to find the
+ listview. This function will return 0 for taken items - see
+ Q3ListViewItem::takeItem()
+*/
+
+Q3ListView * Q3ListViewItem::listView() const
+{
+ const Q3ListViewItem* c = this;
+ while (c && !c->is_root)
+ c = c->parentItem;
+ if (!c)
+ return 0;
+ return ((Q3ListViewPrivate::Root*)c)->theListView();
+}
+
+
+/*!
+ Returns the depth of this item.
+*/
+int Q3ListViewItem::depth() const
+{
+ return parentItem ? parentItem->depth()+1 : -1; // -1 == the hidden root
+}
+
+
+/*!
+ Returns a pointer to the item immediately above this item on the
+ screen. This is usually the item's closest older sibling, but it
+ may also be its parent or its next older sibling's youngest child,
+ or something else if anyoftheabove->height() returns 0. Returns 0
+ if there is no item immediately above this item.
+
+ This function assumes that all parents of this item are open (i.e.
+ that this item is visible, or can be made visible by scrolling).
+
+ This function might be relatively slow because of the tree
+ traversions needed to find the correct item.
+
+ \sa itemBelow() Q3ListView::itemRect()
+*/
+
+Q3ListViewItem * Q3ListViewItem::itemAbove() const
+{
+ if (!parentItem)
+ return 0;
+
+ Q3ListViewItem * c = parentItem;
+ if (c->childItem != this) {
+ c = c->childItem;
+ while(c && c->siblingItem != this)
+ c = c->siblingItem;
+ if (!c)
+ return 0;
+ while(c->isOpen() && c->childItem) {
+ c = c->childItem;
+ while(c->siblingItem)
+ c = c->siblingItem; // assign c's sibling to c
+ }
+ }
+ if (c && (!c->height() || !c->isEnabled()))
+ return c->itemAbove();
+ return c;
+}
+
+
+/*!
+ Returns a pointer to the item immediately below this item on the
+ screen. This is usually the item's eldest child, but it may also
+ be its next younger sibling, its parent's next younger sibling,
+ grandparent's, etc., or something else if anyoftheabove->height()
+ returns 0. Returns 0 if there is no item immediately below this
+ item.
+
+ This function assumes that all parents of this item are open (i.e.
+ that this item is visible or can be made visible by scrolling).
+
+ \sa itemAbove() Q3ListView::itemRect()
+*/
+
+Q3ListViewItem * Q3ListViewItem::itemBelow() const
+{
+ Q3ListViewItem * c = 0;
+ if (isOpen() && childItem) {
+ c = childItem;
+ } else if (siblingItem) {
+ c = siblingItem;
+ } else if (parentItem) {
+ c = const_cast<Q3ListViewItem*>(this);
+ do {
+ c = c->parentItem;
+ } while(c->parentItem && !c->siblingItem);
+ if (c)
+ c = c->siblingItem;
+ }
+ if (c && (!c->height() || !c->isEnabled()))
+ return c->itemBelow();
+ return c;
+}
+
+
+/*!
+ \fn bool Q3ListViewItem::isOpen() const
+
+ Returns true if this list view item has children \e and they are
+ not explicitly hidden; otherwise returns false.
+
+ \sa setOpen()
+*/
+
+/*!
+ Returns the first (top) child of this item, or 0 if this item has
+ no children.
+
+ Note that the children are not guaranteed to be sorted properly.
+ Q3ListView and Q3ListViewItem try to postpone or avoid sorting to
+ the greatest degree possible, in order to keep the user interface
+ snappy.
+
+ \sa nextSibling() sortChildItems()
+*/
+
+Q3ListViewItem* Q3ListViewItem::firstChild() const
+{
+ enforceSortOrder();
+ return childItem;
+}
+
+
+/*!
+ Returns the parent of this item, or 0 if this item has no parent.
+
+ \sa firstChild(), nextSibling()
+*/
+
+Q3ListViewItem* Q3ListViewItem::parent() const
+{
+ if (!parentItem || parentItem->is_root) return 0;
+ return parentItem;
+}
+
+
+/*!
+ \fn Q3ListViewItem* Q3ListViewItem::nextSibling() const
+
+ Returns the sibling item below this item, or 0 if there is no
+ sibling item after this item.
+
+ Note that the siblings are not guaranteed to be sorted properly.
+ Q3ListView and Q3ListViewItem try to postpone or avoid sorting to
+ the greatest degree possible, in order to keep the user interface
+ snappy.
+
+ \sa firstChild() sortChildItems()
+*/
+
+/*!
+ \fn int Q3ListViewItem::childCount () const
+
+ Returns how many children this item has. The count only includes
+ the item's immediate children.
+*/
+
+
+/*!
+ Returns the height of this item in pixels. This does not include
+ the height of any children; totalHeight() returns that.
+*/
+int Q3ListViewItem::height() const
+{
+ Q3ListViewItem * that = (Q3ListViewItem *)this;
+ if (!that->configured) {
+ that->configured = true;
+ that->setup(); // ### virtual non-const function called in const
+ }
+
+ return visible ? ownHeight : 0;
+}
+
+/*!
+ Call this function when the value of width() may have changed for
+ column \a c. Normally, you should call this if text(c) changes.
+ Passing -1 for \a c indicates that all columns may have changed.
+ It is more efficient to pass -1 if two or more columns have
+ changed than to call widthChanged() separately for each one.
+
+ \sa width()
+*/
+void Q3ListViewItem::widthChanged(int c) const
+{
+ Q3ListView *lv = listView();
+ if (lv)
+ lv->widthChanged(this, c);
+}
+
+/*!
+ \fn void Q3ListView::dropped (QDropEvent * e)
+
+ This signal is emitted, when a drop event occurred on the
+ viewport (not onto an item).
+
+ \a e provides all the information about the drop.
+*/
+
+/*!
+ \fn void Q3ListView::selectionChanged()
+
+ This signal is emitted whenever the set of selected items has
+ changed (normally before the screen update). It is available both
+ in \c Single selection and \c Multi selection mode but is most
+ useful in \c Multi selection mode.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+
+ \sa setSelected() Q3ListViewItem::setSelected()
+*/
+
+
+/*!
+ \fn void Q3ListView::pressed(Q3ListViewItem *item)
+
+ This signal is emitted whenever the user presses the mouse button
+ in a list view. \a item is the list view item on which the user
+ pressed the mouse button, or 0 if the user didn't press the mouse
+ on an item.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListView::pressed(Q3ListViewItem *item, const QPoint &pnt, int c)
+
+ \overload
+
+ This signal is emitted whenever the user presses the mouse button
+ in a list view. \a item is the list view item on which the user
+ pressed the mouse button, or 0 if the user didn't press the mouse
+ on an item. \a pnt is the position of the mouse cursor in global
+ coordinates, and \a c is the column where the mouse cursor was
+ when the user pressed the mouse button.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListView::clicked(Q3ListViewItem *item)
+
+ This signal is emitted whenever the user clicks (mouse pressed \e
+ and mouse released) in the list view. \a item is the clicked list
+ view item, or 0 if the user didn't click on an item.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListView::mouseButtonClicked(int button, Q3ListViewItem * item, const QPoint & pos, int c)
+
+ This signal is emitted whenever the user clicks (mouse pressed \e
+ and mouse released) in the list view at position \a pos. \a button
+ is the mouse button that the user pressed, \a item is the clicked
+ list view item or 0 if the user didn't click on an item. If \a
+ item is not 0, \a c is the list view column into which the user
+ pressed; if \a item is 0 \a{c}'s value is undefined.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListView::mouseButtonPressed(int button, Q3ListViewItem * item, const QPoint & pos, int c)
+
+ This signal is emitted whenever the user pressed the mouse button
+ in the list view at position \a pos. \a button is the mouse button
+ which the user pressed, \a item is the pressed list view item or 0
+ if the user didn't press on an item. If \a item is not 0, \a c is
+ the list view column into which the user pressed; if \a item is 0
+ \a{c}'s value is undefined.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListView::clicked(Q3ListViewItem *item, const QPoint &pnt, int c)
+
+ \overload
+
+ This signal is emitted whenever the user clicks (mouse pressed \e
+ and mouse released) in the list view. \a item is the clicked list
+ view item, or 0 if the user didn't click on an item. \a pnt is the
+ position where the user has clicked in global coordinates. If \a
+ item is not 0, \a c is the list view column into which the user
+ pressed; if \a item is 0 \a{c}'s value is undefined.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+*/
+
+/*!
+ \fn void Q3ListView::selectionChanged(Q3ListViewItem *item)
+
+ \overload
+
+ This signal is emitted whenever the selected item has changed in
+ \c Single selection mode (normally after the screen update). The
+ argument is the newly selected \a item.
+
+ In \c Multi selection mode, use the no argument overload of this
+ signal.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+
+ \sa setSelected() Q3ListViewItem::setSelected() currentChanged()
+*/
+
+
+/*!
+ \fn void Q3ListView::currentChanged(Q3ListViewItem *item)
+
+ This signal is emitted whenever the current item has changed
+ (normally after the screen update). The current item is the item
+ responsible for indicating keyboard focus.
+
+ The argument is the newly current \a item, or 0 if the change made
+ no item current. This can happen, for example, if all the items in
+ the list view are deleted.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+
+ \sa setCurrentItem() currentItem()
+*/
+
+
+/*!
+ \fn void Q3ListView::expanded(Q3ListViewItem *item)
+
+ This signal is emitted when \a item has been expanded, i.e. when
+ the children of \a item are shown.
+
+ \sa setOpen() collapsed()
+*/
+
+/*!
+ \fn void Q3ListView::collapsed(Q3ListViewItem *item)
+
+ This signal is emitted when the \a item has been collapsed, i.e.
+ when the children of \a item are hidden.
+
+ \sa setOpen() expanded()
+*/
+
+/*!
+ Processes the mouse press event \a e on behalf of the viewed widget.
+*/
+void Q3ListView::contentsMousePressEvent(QMouseEvent * e)
+{
+ contentsMousePressEventEx(e);
+}
+
+void Q3ListView::contentsMousePressEventEx(QMouseEvent * e)
+{
+ if (!e)
+ return;
+
+ if (!d->ignoreEditAfterFocus)
+ d->startEdit = true;
+ d->ignoreEditAfterFocus = false;
+
+ if (currentItem() && currentItem()->renameBox &&
+ !itemRect(currentItem()).contains(e->pos())) {
+ d->startEdit = false;
+ if (d->defRenameAction == Reject)
+ currentItem()->cancelRename(currentItem()->renameCol);
+ else
+ currentItem()->okRename(currentItem()->renameCol);
+ }
+
+ d->startDragItem = 0;
+ d->dragStartPos = e->pos();
+ QPoint vp = contentsToViewport(e->pos());
+
+ d->ignoreDoubleClick = false;
+ d->buttonDown = true;
+
+ Q3ListViewItem * i = itemAt(vp);
+ d->pressedEmptyArea = e->y() > contentsHeight();
+ if (i && !i->isEnabled())
+ return;
+ if (d->startEdit && (i != currentItem() || (i && !i->isSelected())))
+ d->startEdit = false;
+ Q3ListViewItem *oldCurrent = currentItem();
+
+ if (e->button() == Qt::RightButton && (e->state() & Qt::ControlButton))
+ goto emit_signals;
+
+ if (!i) {
+ if (!(e->state() & Qt::ControlButton))
+ clearSelection();
+ goto emit_signals;
+ } else {
+ // No new anchor when using shift
+ if (!(e->state() & Qt::ShiftButton))
+ d->selectAnchor = i;
+ }
+
+ if ((i->isExpandable() || i->childCount()) &&
+ d->h->mapToLogical(d->h->cellAt(vp.x())) == 0) {
+ int x1 = vp.x() +
+ d->h->offset() -
+ d->h->cellPos(d->h->mapToActual(0));
+ int draw = 0;
+ for (; draw < d->drawables.size(); ++draw)
+ if (d->drawables.at(draw).i == i)
+ break;
+
+ if (draw < d->drawables.size()) {
+ Q3ListViewPrivate::DrawableItem it = d->drawables.at(draw);
+ QStyleOptionQ3ListView opt = getStyleOption(this, i);
+ x1 -= treeStepSize() * (it.l - 1);
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_Q3ListView, &opt,
+ QPoint(x1, e->pos().y()), this);
+ if (ctrl == QStyle::SC_Q3ListViewExpand &&
+ e->type() == style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0,
+ this)) {
+ d->buttonDown = false;
+ if (e->button() == Qt::LeftButton) {
+ bool close = i->isOpen();
+ setOpen(i, !close);
+ // ### Looks dangerous, removed because of reentrance problems
+ // qApp->processEvents();
+ if (!d->focusItem) {
+ d->focusItem = i;
+ repaintItem(d->focusItem);
+ emit currentChanged(d->focusItem);
+ }
+ if (close) {
+ bool newCurrent = false;
+ Q3ListViewItem *ci = d->focusItem;
+ while (ci) {
+ if (ci->parent() && ci->parent() == i) {
+ newCurrent = true;
+ break;
+ }
+ ci = ci->parent();
+ }
+ if (newCurrent) {
+ setCurrentItem(i);
+ }
+ }
+ }
+ d->ignoreDoubleClick = true;
+ d->buttonDown = false;
+ goto emit_signals;
+ }
+ }
+ }
+
+ d->select = d->selectionMode == Multi ? !i->isSelected() : true;
+
+ {// calculate activatedP
+ activatedByClick = true;
+ QPoint topLeft = itemRect(i).topLeft(); //### inefficient?
+ activatedP = vp - topLeft;
+ int xdepth = treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0))
+ + itemMargin();
+ xdepth += d->h->sectionPos(d->h->mapToSection(0));
+ activatedP.rx() -= xdepth;
+ }
+ i->activate();
+ activatedByClick = false;
+
+ if (i != d->focusItem)
+ setCurrentItem(i);
+ else
+ repaintItem(i);
+
+ d->pressedSelected = i && i->isSelected();
+
+ if (i->isSelectable() && selectionMode() != NoSelection) {
+ if (selectionMode() == Single)
+ setSelected(i, true);
+ else if (selectionMode() == Multi)
+ setSelected(i, d->select);
+ else if (selectionMode() == Extended) {
+ bool changed = false;
+ if (!(e->state() & (Qt::ControlButton | Qt::ShiftButton))) {
+ if (!i->isSelected()) {
+ bool blocked = signalsBlocked();
+ blockSignals(true);
+ clearSelection();
+ blockSignals(blocked);
+ i->setSelected(true);
+ changed = true;
+ }
+ } else {
+ if (e->state() & Qt::ShiftButton)
+ d->pressedSelected = false;
+ if ((e->state() & Qt::ControlButton) && !(e->state() & Qt::ShiftButton) && i) {
+ i->setSelected(!i->isSelected());
+ changed = true;
+ d->pressedSelected = false;
+ } else if (!oldCurrent || !i || oldCurrent == i) {
+ if ((bool)i->selected != d->select) {
+ changed = true;
+ i->setSelected(d->select);
+ }
+ // Shift pressed in Extended mode ---
+ } else {
+ changed = selectRange(i, oldCurrent, d->selectAnchor);
+ }
+ }
+ if (changed) {
+ triggerUpdate();
+ emit selectionChanged();
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+#endif
+ }
+ }
+ }
+
+ emit_signals:
+
+ if (i && !d->buttonDown &&
+ vp.x() + contentsX() < itemMargin() + (i->depth() + (rootIsDecorated() ? 1 : 0)) * treeStepSize())
+ i = 0;
+ d->pressedItem = i;
+
+ int c = i ? d->h->mapToLogical(d->h->cellAt(vp.x())) : -1;
+ if (!i || (i && i->isEnabled())) {
+ emit pressed(i);
+ emit pressed(i, viewport()->mapToGlobal(vp), c);
+ }
+ emit mouseButtonPressed(e->button(), i, viewport()->mapToGlobal(vp), c);
+
+ if (e->button() == Qt::RightButton && i == d->pressedItem) {
+ if (!i && !(e->state() & Qt::ControlButton))
+ clearSelection();
+
+ emit rightButtonPressed(i, viewport()->mapToGlobal(vp), c);
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3ListView::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+ if (!receivers(SIGNAL(contextMenuRequested(Q3ListViewItem*,QPoint,int)))) {
+ e->ignore();
+ return;
+ }
+ if (e->reason() == QContextMenuEvent::Keyboard) {
+ Q3ListViewItem *item = currentItem();
+ if (item) {
+ QRect r = itemRect(item);
+ QPoint p = r.topLeft();
+ if (allColumnsShowFocus())
+ p += QPoint(width() / 2, (r.height() / 2));
+ else
+ p += QPoint(columnWidth(0) / 2, (r.height() / 2));
+ p.rx() = qMax(0, p.x());
+ p.rx() = qMin(visibleWidth(), p.x());
+ emit contextMenuRequested(item, viewport()->mapToGlobal(p), -1);
+ }
+ } else {
+ QPoint vp = contentsToViewport(e->pos());
+ Q3ListViewItem * i = itemAt(vp);
+ int c = i ? d->h->mapToLogical(d->h->cellAt(vp.x())) : -1;
+ emit contextMenuRequested(i, viewport()->mapToGlobal(vp), c);
+ }
+}
+
+/*!
+ Processes the mouse release event \a e on behalf of the viewed widget.
+*/
+void Q3ListView::contentsMouseReleaseEvent(QMouseEvent * e)
+{
+ contentsMouseReleaseEventEx(e);
+}
+
+void Q3ListView::contentsMouseReleaseEventEx(QMouseEvent * e)
+{
+ d->startDragItem = 0;
+ bool emitClicked = !d->pressedItem || d->buttonDown;
+ d->buttonDown = false;
+ // delete and disconnect autoscroll timer, if we have one
+ if (d->scrollTimer) {
+ disconnect(d->scrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+ d->scrollTimer->stop();
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ }
+
+ if (!e)
+ return;
+
+ if (d->selectionMode == Extended &&
+ d->focusItem == d->pressedItem &&
+ d->pressedSelected && d->focusItem &&
+ e->button() == Qt::LeftButton) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ clearSelection();
+ blockSignals(block);
+ d->focusItem->setSelected(true);
+ emit selectionChanged();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection);
+#endif
+ }
+
+ QPoint vp = contentsToViewport(e->pos());
+ Q3ListViewItem *i = itemAt(vp);
+ if (i && !i->isEnabled())
+ return;
+
+ if (i && i == d->pressedItem && (i->isExpandable() || i->childCount()) &&
+ !d->h->mapToLogical(d->h->cellAt(vp.x())) && e->button() == Qt::LeftButton &&
+ e->type() == style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this)) {
+ int draw = 0;
+ for (; draw < d->drawables.size(); ++draw)
+ if (d->drawables.at(draw).i == i)
+ break;
+ if (draw < d->drawables.size()) {
+ int x1 = vp.x() + d->h->offset() - d->h->cellPos(d->h->mapToActual(0)) -
+ (treeStepSize() * (d->drawables.at(draw).l - 1));
+ QStyleOptionQ3ListView opt = getStyleOption(this, i);
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_Q3ListView, &opt,
+ QPoint(x1, e->pos().y()), this);
+ if (ctrl == QStyle::SC_Q3ListViewExpand) {
+ bool close = i->isOpen();
+ setOpen(i, !close);
+ // ### Looks dangerous, removed because of reentrance problems
+ // qApp->processEvents();
+ if (!d->focusItem) {
+ d->focusItem = i;
+ repaintItem(d->focusItem);
+ emit currentChanged(d->focusItem);
+ }
+ if (close) {
+ bool newCurrent = false;
+ Q3ListViewItem *ci = d->focusItem;
+ while (ci) {
+ if (ci->parent() && ci->parent() == i) {
+ newCurrent = true;
+ break;
+ }
+ ci = ci->parent();
+ }
+ if (newCurrent)
+ setCurrentItem(i);
+ d->ignoreDoubleClick = true;
+ }
+ }
+ }
+ }
+
+ if (i == d->pressedItem && i && i->isSelected() && e->button() == Qt::LeftButton && d->startEdit) {
+ QRect r = itemRect(currentItem());
+ r = QRect(viewportToContents(r.topLeft()), r.size());
+ d->pressedColumn = header()->sectionAt( e->pos().x());
+ r.setLeft(header()->sectionPos(d->pressedColumn));
+ r.setWidth(header()->sectionSize(d->pressedColumn) - 1);
+ if (d->pressedColumn == 0)
+ r.setLeft(r.left() + itemMargin() + (currentItem()->depth() +
+ (rootIsDecorated() ? 1 : 0)) * treeStepSize() - 1);
+ if (r.contains(e->pos()) &&
+ !(e->state() & (Qt::ShiftButton | Qt::ControlButton)))
+ d->renameTimer->start(QApplication::doubleClickInterval(), true);
+ }
+ if (i && vp.x() + contentsX() < itemMargin() + (i->depth() + (rootIsDecorated() ? 1 : 0)) * treeStepSize())
+ i = 0;
+ emitClicked = emitClicked && d->pressedItem == i;
+ d->pressedItem = 0;
+
+ if (emitClicked) {
+ if (!i || (i && i->isEnabled())) {
+ emit clicked(i);
+ emit clicked(i, viewport()->mapToGlobal(vp), d->h->mapToLogical(d->h->cellAt(vp.x())));
+ }
+ emit mouseButtonClicked(e->button(), i, viewport()->mapToGlobal(vp),
+ i ? d->h->mapToLogical(d->h->cellAt(vp.x())) : -1);
+
+ if (e->button() == Qt::RightButton) {
+ if (!i) {
+ if (!(e->state() & Qt::ControlButton))
+ clearSelection();
+ emit rightButtonClicked(0, viewport()->mapToGlobal(vp), -1);
+ return;
+ }
+
+ int c = d->h->mapToLogical(d->h->cellAt(vp.x()));
+ emit rightButtonClicked(i, viewport()->mapToGlobal(vp), c);
+ }
+ }
+}
+
+
+/*!
+ Processes the mouse double-click event \a e on behalf of the viewed widget.
+*/
+void Q3ListView::contentsMouseDoubleClickEvent(QMouseEvent * e)
+{
+ d->renameTimer->stop();
+ d->startEdit = false;
+ if (!e || e->button() != Qt::LeftButton)
+ return;
+
+ // ensure that the following mouse moves and eventual release is
+ // ignored.
+ d->buttonDown = false;
+
+ if (d->ignoreDoubleClick) {
+ d->ignoreDoubleClick = false;
+ return;
+ }
+
+ QPoint vp = contentsToViewport(e->pos());
+
+ Q3ListViewItem * i = itemAt(vp);
+
+ // we emit doubleClicked when the item is null (or enabled) to be consistent with
+ // rightButtonClicked etc.
+ if (!i || i->isEnabled()) {
+ int c = d->h->mapToLogical(d->h->cellAt(vp.x()));
+ emit doubleClicked(i, viewport()->mapToGlobal(vp), c);
+ }
+
+ if (!i || !i->isEnabled())
+ return;
+
+ if (!i->isOpen()) {
+ if (i->isExpandable() || i->childCount())
+ setOpen(i, true);
+ } else {
+ setOpen(i, false);
+ }
+
+ // we emit the 'old' obsolete doubleClicked only if the item is not null and enabled
+ emit doubleClicked(i);
+}
+
+
+/*!
+ Processes the mouse move event \a e on behalf of the viewed widget.
+*/
+void Q3ListView::contentsMouseMoveEvent(QMouseEvent * e)
+{
+ if (!e)
+ return;
+
+ bool needAutoScroll = false;
+
+ QPoint vp = contentsToViewport(e->pos());
+
+ Q3ListViewItem * i = itemAt(vp);
+ if (i && !i->isEnabled())
+ return;
+ if (i != d->highlighted &&
+ !(d->pressedItem &&
+ (d->pressedItem->isSelected() || d->selectionMode == NoSelection) &&
+ d->pressedItem->dragEnabled())) {
+
+ if (i) {
+ emit onItem(i);
+ } else {
+ emit onViewport();
+ }
+ d->highlighted = i;
+ }
+
+ if (d->startDragItem)
+ i = d->startDragItem;
+
+ if (!d->buttonDown ||
+ ((e->state() & Qt::LeftButton) != Qt::LeftButton &&
+ (e->state() & Qt::MidButton) != Qt::MidButton &&
+ (e->state() & Qt::RightButton) != Qt::RightButton))
+ return;
+
+ if (d->pressedItem &&
+ (d->pressedItem->isSelected() || d->selectionMode == NoSelection) &&
+ d->pressedItem->dragEnabled()) {
+
+ if (!d->startDragItem) {
+ setSelected(d->pressedItem, true);
+ d->startDragItem = d->pressedItem;
+ }
+ if ((d->dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) {
+ d->buttonDown = false;
+#ifndef QT_NO_DRAGANDDROP
+ startDrag();
+#endif
+ }
+ return;
+ }
+
+ // check, if we need to scroll
+ if (vp.y() > visibleHeight() || vp.y() < 0)
+ needAutoScroll = true;
+
+ // if we need to scroll and no autoscroll timer is started,
+ // connect the timer
+ if (needAutoScroll && !d->scrollTimer) {
+ d->scrollTimer = new QTimer(this);
+ connect(d->scrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+ d->scrollTimer->start(100, false);
+ // call it once manually
+ doAutoScroll(vp);
+ }
+
+ // if we don't need to autoscroll
+ if (!needAutoScroll) {
+ // if there is a autoscroll timer, delete it
+ if (d->scrollTimer) {
+ disconnect(d->scrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+ d->scrollTimer->stop();
+ delete d->scrollTimer;
+ d->scrollTimer = 0;
+ }
+ // call this to select an item (using the pos from the event)
+ doAutoScroll(vp);
+ }
+}
+
+
+/*!
+ This slot handles auto-scrolling when the mouse button is pressed
+ and the mouse is outside the widget.
+*/
+void Q3ListView::doAutoScroll()
+{
+ doAutoScroll(QPoint());
+}
+
+/*
+ Handles auto-scrolling when the mouse button is pressed
+ and the mouse is outside the widget.
+
+ If cursorPos is (0,0) (isNull == true) it uses the current QCursor::pos, otherwise it uses cursorPos
+*/
+void Q3ListView::doAutoScroll(const QPoint &cursorPos)
+{
+ QPoint pos = cursorPos.isNull() ? viewport()->mapFromGlobal(QCursor::pos()) : cursorPos;
+ if (!d->focusItem || (d->pressedEmptyArea && pos.y() > contentsHeight()))
+ return;
+
+ bool down = pos.y() > itemRect(d->focusItem).y();
+
+ int g = pos.y() + contentsY();
+
+ if (down && pos.y() > height() )
+ g = height() + contentsY();
+ else if (pos.y() < 0)
+ g = contentsY();
+
+ Q3ListViewItem *c = d->focusItem, *old = 0;
+ Q3ListViewItem *oldCurrent = c;
+ if (down) {
+ int y = itemRect(d->focusItem).y() + contentsY();
+ while(c && y + c->height() <= g) {
+ y += c->height();
+ old = c;
+ c = c->itemBelow();
+ }
+ if (!c && old)
+ c = old;
+ } else {
+ int y = itemRect(d->focusItem).y() + contentsY();
+ while(c && y >= g) {
+ old = c;
+ c = c->itemAbove();
+ if (c)
+ y -= c->height();
+ }
+ if (!c && old)
+ c = old;
+ }
+
+ if (!c || c == d->focusItem)
+ return;
+
+ if (d->focusItem) {
+ if (d->selectionMode == Multi) {
+ // also (de)select the ones in between
+ Q3ListViewItem * b = d->focusItem;
+ bool down = (itemPos(c) > itemPos(b));
+ while(b && b != c) {
+ if (b->isSelectable())
+ setSelected(b, d->select);
+ b = down ? b->itemBelow() : b->itemAbove();
+ }
+ if (c->isSelectable())
+ setSelected(c, d->select);
+ } else if (d->selectionMode == Extended) {
+ if (selectRange(c, oldCurrent, d->selectAnchor)) {
+ triggerUpdate();
+ emit selectionChanged();
+ }
+ }
+ }
+
+ setCurrentItem(c);
+ d->visibleTimer->start(1, true);
+}
+
+/*!
+ \reimp
+*/
+
+void Q3ListView::focusInEvent(QFocusEvent *e)
+{
+ d->inMenuMode = false;
+ if (d->focusItem) {
+ repaintItem(d->focusItem);
+ } else if (firstChild() && e->reason() != Qt::MouseFocusReason) {
+ d->focusItem = firstChild();
+ emit currentChanged(d->focusItem);
+ repaintItem(d->focusItem);
+ }
+ if (e->reason() == Qt::MouseFocusReason) {
+ d->ignoreEditAfterFocus = true;
+ d->startEdit = false;
+ }
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) {
+ viewport()->repaint();
+ }
+}
+
+/*!
+ \reimp
+*/
+QVariant Q3ListView::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ if (query == Qt::ImMicroFocus) {
+ QRect mfrect = itemRect(d->focusItem);
+ if (mfrect.isValid() && header() && header()->isVisible())
+ mfrect.moveBy(0, header()->height());
+ return mfrect;
+ }
+ return QWidget::inputMethodQuery(query);
+}
+
+/*!
+ \reimp
+*/
+
+void Q3ListView::focusOutEvent(QFocusEvent *e)
+{
+ if (e->reason() == Qt::PopupFocusReason && d->buttonDown)
+ d->buttonDown = false;
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) {
+ d->inMenuMode =
+ e->reason() == Qt::PopupFocusReason
+ || (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar"));
+ if (!d->inMenuMode) {
+ viewport()->repaint();
+ }
+ }
+
+ if (d->focusItem)
+ repaintItem(d->focusItem);
+}
+
+
+/*!
+ \reimp
+*/
+
+void Q3ListView::keyPressEvent(QKeyEvent * e)
+{
+ if (currentItem() && currentItem()->renameBox)
+ return;
+ if (!firstChild()) {
+ e->ignore();
+ return; // subclass bug
+ }
+
+ Q3ListViewItem* oldCurrent = currentItem();
+ if (!oldCurrent) {
+ setCurrentItem(firstChild());
+ if (d->selectionMode == Single)
+ setSelected(firstChild(), true);
+ return;
+ }
+
+ Q3ListViewItem * i = currentItem();
+ Q3ListViewItem *old = i;
+
+ QRect r(itemRect(i));
+ Q3ListViewItem * i2;
+
+ bool singleStep = false;
+ bool selectCurrent = true;
+ bool wasNavigation = true;
+
+ switch(e->key()) {
+ case Qt::Key_Backspace:
+ case Qt::Key_Delete:
+ d->currentPrefix.truncate(0);
+ break;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ d->currentPrefix.truncate(0);
+ if (i && !i->isSelectable() && i->isEnabled() &&
+ (i->childCount() || i->isExpandable() || i->isOpen())) {
+ i->setOpen(!i->isOpen());
+ return;
+ }
+ e->ignore();
+ if (currentItem() && !currentItem()->isEnabled())
+ break;
+ emit returnPressed(currentItem());
+ // do NOT accept. QDialog.
+ return;
+ case Qt::Key_Down:
+ selectCurrent = false;
+ i = i->itemBelow();
+ d->currentPrefix.truncate(0);
+ singleStep = true;
+ break;
+ case Qt::Key_Up:
+ selectCurrent = false;
+ i = i->itemAbove();
+ d->currentPrefix.truncate(0);
+ singleStep = true;
+ break;
+ case Qt::Key_Home:
+ selectCurrent = false;
+ i = firstChild();
+ if (!i->height() || !i->isEnabled())
+ i = i->itemBelow();
+ d->currentPrefix.truncate(0);
+ break;
+ case Qt::Key_End:
+ selectCurrent = false;
+ i = firstChild();
+ while (i->nextSibling() && i->nextSibling()->height() && i->nextSibling()->isEnabled())
+ i = i->nextSibling();
+ while (i->itemBelow())
+ i = i->itemBelow();
+ d->currentPrefix.truncate(0);
+ break;
+ case Qt::Key_Next:
+ selectCurrent = false;
+ i2 = itemAt(QPoint(0, visibleHeight()-1));
+ if (i2 == i || !r.isValid() ||
+ visibleHeight() <= itemRect(i).bottom()) {
+ if (i2)
+ i = i2;
+ int left = visibleHeight();
+ while((i2 = i->itemBelow()) != 0 && left > i2->height()) {
+ left -= i2->height();
+ i = i2;
+ }
+ } else {
+ if (!i2) {
+ // list is shorter than the view, goto last item
+ while((i2 = i->itemBelow()) != 0)
+ i = i2;
+ } else {
+ i = i2;
+ }
+ }
+ d->currentPrefix.truncate(0);
+ break;
+ case Qt::Key_Prior:
+ selectCurrent = false;
+ i2 = itemAt(QPoint(0, 0));
+ if (i == i2 || !r.isValid() || r.top() <= 0) {
+ if (i2)
+ i = i2;
+ int left = visibleHeight();
+ while((i2 = i->itemAbove()) != 0 && left > i2->height()) {
+ left -= i2->height();
+ i = i2;
+ }
+ } else {
+ i = i2;
+ }
+ d->currentPrefix.truncate(0);
+ break;
+ case Qt::Key_Plus:
+ d->currentPrefix.truncate(0);
+ if ( !i->isOpen() && (i->isExpandable() || i->childCount()))
+ setOpen(i, true);
+ else
+ return;
+ break;
+ case Qt::Key_Right:
+ d->currentPrefix.truncate(0);
+ if (i->isOpen() && i->childItem) {
+ i = i->childItem;
+ } else if (!i->isOpen() && (i->isExpandable() || i->childCount())) {
+ setOpen(i, true);
+ } else if (contentsX() + visibleWidth() < contentsWidth()) {
+ horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepAdd);
+ return;
+ } else {
+ return;
+ }
+ break;
+ case Qt::Key_Minus:
+ d->currentPrefix.truncate(0);
+ if (i->isOpen())
+ setOpen(i, false);
+ else
+ return;
+ break;
+ case Qt::Key_Left:
+ d->currentPrefix.truncate(0);
+ if (i->isOpen()) {
+ setOpen(i, false);
+ } else if (i->parentItem && i->parentItem != d->r) {
+ i = i->parentItem;
+ } else if (contentsX()) {
+ horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepSub);
+ return;
+ } else {
+ return;
+ }
+ break;
+ case Qt::Key_Space:
+ activatedByClick = false;
+ d->currentPrefix.truncate(0);
+ if (currentItem() && !currentItem()->isEnabled())
+ break;
+ i->activate();
+ if (i->isSelectable() && (d->selectionMode == Multi || d->selectionMode == Extended)) {
+ setSelected(i, !i->isSelected());
+ d->currentPrefix.truncate(0);
+ }
+ emit spacePressed(currentItem());
+ break;
+ case Qt::Key_Escape:
+ e->ignore(); // For QDialog
+ return;
+ case Qt::Key_F2:
+ if (currentItem() && currentItem()->renameEnabled(0))
+ currentItem()->startRename(0);
+ default:
+ if (e->text().length() > 0 && e->text()[0].isPrint()) {
+ selectCurrent = false;
+ wasNavigation = false;
+ QString input(d->currentPrefix);
+ Q3ListViewItem * keyItem = i;
+ QTime now(QTime::currentTime());
+ bool tryFirst = true;
+ while(keyItem) {
+ // try twice, first with the previous string and this char
+ if (d->currentPrefixTime.msecsTo(now) <= 400)
+ input = input + e->text().toLower();
+ else
+ input = e->text().toLower();
+ if (input.length() == e->text().length()) {
+ if (keyItem->itemBelow()) {
+ keyItem = keyItem->itemBelow();
+ tryFirst = true;
+ } else {
+ keyItem = firstChild();
+ tryFirst = false;
+ }
+ }
+ QString keyItemKey;
+ QString prefix;
+ while(keyItem) {
+ keyItemKey = QString::null;
+ // Look first in the sort column, then left to right
+ if (d->sortcolumn != Unsorted)
+ keyItemKey = keyItem->text(d->sortcolumn);
+ for (int col = 0; col < d->h->count() && keyItemKey.isNull(); ++col)
+ keyItemKey = keyItem->text(d->h->mapToSection(col));
+ if (!keyItemKey.isEmpty()) {
+ prefix = keyItemKey;
+ prefix.truncate(input.length());
+ prefix = prefix.toLower();
+ if (prefix == input) {
+ d->currentPrefix = input;
+ d->currentPrefixTime = now;
+ i = keyItem;
+ // nonoptimal double-break...
+ keyItem = 0;
+ input.truncate(0);
+ tryFirst = false;
+ }
+ }
+ if (keyItem)
+ keyItem = keyItem->itemBelow();
+ if (!keyItem && tryFirst) {
+ keyItem = firstChild();
+ tryFirst = false;
+ }
+ }
+ // then, if appropriate, with just this character
+ if (input.length() > e->text().length()) {
+ input.truncate(0);
+ keyItem = i;
+ }
+ }
+ } else {
+ d->currentPrefix.truncate(0);
+ if (e->state() & Qt::ControlButton) {
+ d->currentPrefix.clear();
+ switch (e->key()) {
+ case Qt::Key_A:
+ selectAll(true);
+ break;
+ }
+ }
+ e->ignore();
+ return;
+ }
+ }
+
+ if (!i)
+ return;
+
+ if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)
+ d->selectAnchor = i;
+
+ setCurrentItem(i);
+ if (i->isSelectable())
+ handleItemChange(old, wasNavigation && (e->state() & Qt::ShiftButton),
+ wasNavigation && (e->state() & Qt::ControlButton));
+
+ if (d->focusItem && !d->focusItem->isSelected() && d->selectionMode == Single && selectCurrent)
+ setSelected(d->focusItem, true);
+
+ if (singleStep)
+ d->visibleTimer->start(1, true);
+ else
+ ensureItemVisible(i);
+}
+
+
+/*!
+ Returns the list view item at \a viewPos. Note that \a viewPos is
+ in the viewport()'s coordinate system, not in the list view's own,
+ much larger, coordinate system.
+
+ itemAt() returns 0 if there is no such item.
+
+ Note that you also get the pointer to the item if \a viewPos
+ points to the root decoration (see setRootIsDecorated()) of the
+ item. To check whether or not \a viewPos is on the root decoration
+ of the item, you can do something like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 4
+
+ This might be interesting if you use this function to find out
+ where the user clicked and if you want to start a drag (which you
+ do not want to do if the user clicked onto the root decoration of
+ an item).
+
+ \sa itemPos() itemRect() viewportToContents()
+*/
+
+Q3ListViewItem * Q3ListView::itemAt(const QPoint & viewPos) const
+{
+ if (viewPos.x() > contentsWidth() - contentsX())
+ return 0;
+
+ if (d->drawables.isEmpty())
+ buildDrawableList();
+
+ int g = viewPos.y() + contentsY();
+
+ for (int i = 0; i < d->drawables.size(); ++i) {
+ Q3ListViewPrivate::DrawableItem c = d->drawables.at(i);
+ if (c.y + c.i->height() > g
+ && c.i->isVisible() && (!c.i->parent() || c.i->parent()->isVisible()))
+ return c.y <= g ? c.i : 0;
+ }
+ return 0;
+}
+
+
+/*!
+ Returns the y-coordinate of \a item in the list view's coordinate
+ system. This function is normally much slower than itemAt() but it
+ works for all items, whereas itemAt() normally works only for
+ items on the screen.
+
+ This is a thin wrapper around Q3ListViewItem::itemPos().
+
+ \sa itemAt() itemRect()
+*/
+
+int Q3ListView::itemPos(const Q3ListViewItem * item)
+{
+ return item ? item->itemPos() : 0;
+}
+
+
+/*!
+ \property Q3ListView::multiSelection
+ \brief whether the list view is in multi-selection or extended-selection mode
+
+ If you enable multi-selection, \c Multi, mode, it is possible to
+ specify whether or not this mode should be extended. \c Extended
+ means that the user can select multiple items only when pressing
+ the Shift or Ctrl key at the same time.
+
+ The default selection mode is \c Single.
+
+ \sa selectionMode()
+*/
+
+void Q3ListView::setMultiSelection(bool enable)
+{
+ if (!enable)
+ d->selectionMode = Q3ListView::Single;
+ else if ( d->selectionMode != Multi && d->selectionMode != Extended)
+ d->selectionMode = Q3ListView::Multi;
+}
+
+bool Q3ListView::isMultiSelection() const
+{
+ return d->selectionMode == Q3ListView::Extended || d->selectionMode == Q3ListView::Multi;
+}
+
+/*!
+ \property Q3ListView::selectionMode
+ \brief the list view's selection mode
+
+ The mode can be \c Single (the default), \c Extended, \c Multi or
+ \c NoSelection.
+
+ \sa multiSelection
+*/
+
+void Q3ListView::setSelectionMode(SelectionMode mode)
+{
+ if (d->selectionMode == mode)
+ return;
+
+ if ((d->selectionMode == Multi || d->selectionMode == Extended) &&
+ (mode == Q3ListView::Single || mode == Q3ListView::NoSelection)){
+ clearSelection();
+ if ((mode == Q3ListView::Single) && currentItem())
+ currentItem()->selected = true;
+ }
+
+ d->selectionMode = mode;
+}
+
+Q3ListView::SelectionMode Q3ListView::selectionMode() const
+{
+ return d->selectionMode;
+}
+
+
+/*!
+ If \a selected is true the \a item is selected; otherwise it is
+ unselected.
+
+ If the list view is in \c Single selection mode and \a selected is
+ true, the currently selected item is unselected and \a item is
+ made current. Unlike Q3ListViewItem::setSelected(), this function
+ updates the list view as necessary and emits the
+ selectionChanged() signals.
+
+ \sa isSelected() setMultiSelection() isMultiSelection()
+ setCurrentItem(), setSelectionAnchor()
+*/
+
+void Q3ListView::setSelected(Q3ListViewItem * item, bool selected)
+{
+ if (!item || item->isSelected() == selected ||
+ !item->isSelectable() || selectionMode() == NoSelection)
+ return;
+
+ bool emitHighlighted = false;
+ if (selectionMode() == Single && d->focusItem != item) {
+ Q3ListViewItem *o = d->focusItem;
+ if (d->focusItem && d->focusItem->selected)
+ d->focusItem->setSelected(false);
+ d->focusItem = item;
+ if (o)
+ repaintItem(o);
+ emitHighlighted = true;
+ }
+
+ item->setSelected(selected);
+
+ repaintItem(item);
+
+ if (d->selectionMode == Single && selected)
+ emit selectionChanged(item);
+ emit selectionChanged();
+
+ if (emitHighlighted)
+ emit currentChanged(d->focusItem);
+}
+
+/*!
+ Sets the selection anchor to \a item, if \a item is selectable.
+
+ The selection anchor is the item that remains selected when
+ Shift-selecting with either mouse or keyboard in \c Extended
+ selection mode.
+
+ \sa setSelected()
+*/
+
+void Q3ListView::setSelectionAnchor(Q3ListViewItem *item)
+{
+ if (item && item->isSelectable())
+ d->selectAnchor = item;
+}
+
+/*!
+ Sets all the items to be not selected, updates the list view as
+ necessary, and emits the selectionChanged() signals. Note that for
+ \c Multi selection list views this function needs to iterate over
+ \e all items.
+
+ \sa setSelected(), setMultiSelection()
+*/
+
+void Q3ListView::clearSelection()
+{
+ selectAll(false);
+}
+
+/*!
+ If \a select is true, all the items get selected; otherwise all
+ the items get unselected. This only works in the selection modes \c
+ Multi and \c Extended. In \c Single and \c NoSelection mode the
+ selection of the current item is just set to \a select.
+*/
+
+void Q3ListView::selectAll(bool select)
+{
+ if (d->selectionMode == Multi || d->selectionMode == Extended) {
+ bool b = signalsBlocked();
+ blockSignals(true);
+ bool anything = false;
+ Q3ListViewItemIterator it(this);
+ while (it.current()) {
+ Q3ListViewItem *i = it.current();
+ if ((bool)i->selected != select) {
+ i->setSelected(select);
+ anything = true;
+ }
+ ++it;
+ }
+ blockSignals(b);
+ if (anything) {
+ emit selectionChanged();
+ triggerUpdate();
+ }
+ } else if (d->focusItem) {
+ Q3ListViewItem * i = d->focusItem;
+ setSelected(i, select);
+ }
+}
+
+/*!
+ Inverts the selection. Only works in \c Multi and \c Extended
+ selection modes.
+*/
+
+void Q3ListView::invertSelection()
+{
+ if (d->selectionMode == Single ||
+ d->selectionMode == NoSelection)
+ return;
+
+ bool b = signalsBlocked();
+ blockSignals(true);
+ Q3ListViewItemIterator it(this);
+ for (; it.current(); ++it)
+ it.current()->setSelected(!it.current()->isSelected());
+ blockSignals(b);
+ emit selectionChanged();
+ triggerUpdate();
+}
+
+
+/*!
+ Returns true if the list view item \a i is selected; otherwise
+ returns false.
+
+ \sa Q3ListViewItem::isSelected()
+*/
+
+bool Q3ListView::isSelected(const Q3ListViewItem * i) const
+{
+ return i ? i->isSelected() : false;
+}
+
+
+/*!
+ Returns the selected item if the list view is in \c Single
+ selection mode and an item is selected.
+
+ If no items are selected or the list view is not in \c Single
+ selection mode this function returns 0.
+
+ \sa setSelected() setMultiSelection()
+*/
+
+Q3ListViewItem * Q3ListView::selectedItem() const
+{
+ if (d->selectionMode != Single)
+ return 0;
+ if (d->focusItem && d->focusItem->isSelected())
+ return d->focusItem;
+ return 0;
+}
+
+
+/*!
+ Sets item \a i to be the current item and repaints appropriately
+ (i.e. highlights the item). The current item is used for keyboard
+ navigation and focus indication; it is independent of any selected
+ items, although a selected item can also be the current item.
+
+ \sa currentItem() setSelected()
+*/
+
+void Q3ListView::setCurrentItem(Q3ListViewItem * i)
+{
+ if (!i || d->focusItem == i || !i->isEnabled())
+ return;
+
+ if (currentItem() && currentItem()->renameBox) {
+ if (d->defRenameAction == Reject)
+ currentItem()->cancelRename(currentItem()->renameCol);
+ else
+ currentItem()->okRename(currentItem()->renameCol);
+ }
+
+ Q3ListViewItem * prev = d->focusItem;
+ d->focusItem = i;
+
+ if (i != prev) {
+ if (i && d->selectionMode == Single) {
+ bool changed = false;
+ if (prev && prev->selected) {
+ changed = true;
+ prev->setSelected(false);
+ }
+ if (i && !i->selected && d->selectionMode != NoSelection && i->isSelectable()) {
+ i->setSelected(true);
+ changed = true;
+ emit selectionChanged(i);
+ }
+ if (changed)
+ emit selectionChanged();
+ }
+
+ if (i)
+ repaintItem(i);
+ if (prev)
+ repaintItem(prev);
+ emit currentChanged(i);
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), indexOfItem(i), QAccessible::Focus);
+#endif
+ }
+}
+
+
+/*!
+ Returns the current item, or 0 if there isn't one.
+
+ \sa setCurrentItem()
+*/
+
+Q3ListViewItem * Q3ListView::currentItem() const
+{
+ return d->focusItem;
+}
+
+
+/*!
+ Returns the rectangle on the screen that item \a item occupies in
+ viewport()'s coordinates, or an invalid rectangle if \a item is 0 or
+ is not currently visible.
+
+ The rectangle returned does not include any children of the
+ rectangle (i.e. it uses Q3ListViewItem::height(), rather than
+ Q3ListViewItem::totalHeight()). If you want the rectangle to
+ include children you can use something like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 5
+
+ Note the way it avoids too-high rectangles. totalHeight() can be
+ much larger than the window system's coordinate system allows.
+
+ itemRect() is comparatively slow. It's best to call it only for
+ items that are probably on-screen.
+*/
+
+QRect Q3ListView::itemRect(const Q3ListViewItem * item) const
+{
+ if (d->drawables.isEmpty())
+ buildDrawableList();
+
+ for (int i = 0; i < d->drawables.size(); ++i) {
+ const Q3ListViewPrivate::DrawableItem &c = d->drawables.at(i);
+ if (c.i == item) {
+ int y = c.y - contentsY();
+ if (y + c.i->height() >= 0 && y < ((Q3ListView *)this)->visibleHeight()) {
+ return QRect(-contentsX(), y, d->h->width(), c.i->height());;
+ }
+ }
+ }
+
+ return QRect(0, 0, -1, -1);
+}
+
+
+/*!
+ \fn void Q3ListView::doubleClicked(Q3ListViewItem *item)
+
+ This signal is emitted whenever an item is double-clicked. It's
+ emitted on the second button press, not the second button release.
+ \a item is the list view item on which the user did the
+ double-click.
+*/
+
+/*!
+ \fn void Q3ListView::doubleClicked(Q3ListViewItem *item, const
+ QPoint& point, int column)
+
+ This signal is emitted when a double-click occurs. It's emitted on
+ the second button press, not the second button release. The \a
+ item is the Q3ListViewItem the button was double-clicked on (which
+ could be 0 if it wasn't double-clicked on an item). The \a point
+ where the double-click occurred is given in global coordinates. If
+ an item was double-clicked on, \a column is the column within the
+ item that was double-clicked; otherwise \a column is -1.
+
+ \warning Do not delete any Q3ListViewItem objects in slots
+ connected to this signal.
+*/
+
+
+/*!
+ \fn void Q3ListView::returnPressed(Q3ListViewItem *item)
+
+ This signal is emitted when Enter or Return is pressed. The
+ \a item parameter is the currentItem().
+*/
+
+/*!
+ \fn void Q3ListView::spacePressed(Q3ListViewItem *item)
+
+ This signal is emitted when Space is pressed. The \a item
+ parameter is the currentItem().
+*/
+
+
+/*!
+ Sets the list view to be sorted by column \a column in ascending
+ order if \a ascending is true or descending order if it is false.
+
+ If \a column is -1, sorting is disabled and the user cannot sort
+ columns by clicking on the column headers. If \a column is larger
+ than the number of columns the user must click on a column
+ header to sort the list view.
+*/
+
+void Q3ListView::setSorting(int column, bool ascending)
+{
+ if (column == -1)
+ column = Unsorted;
+
+ if (d->sortcolumn == column && d->ascending == ascending)
+ return;
+
+ d->ascending = ascending;
+ d->sortcolumn = column;
+ if (d->sortcolumn != Unsorted && d->sortIndicator)
+ d->h->setSortIndicator(d->sortcolumn, d->ascending);
+ else
+ d->h->setSortIndicator(-1);
+
+ triggerUpdate();
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(viewport(), 0, QAccessible::ObjectReorder);
+#endif
+}
+
+/*!
+ Sets the \a column the list view is sorted by.
+
+ Sorting is triggered by choosing a header section.
+*/
+
+void Q3ListView::changeSortColumn(int column)
+{
+ if (isRenaming()) {
+ if (d->defRenameAction == Q3ListView::Reject) {
+ currentItem()->cancelRename(currentItem()->renameCol);
+ } else {
+ currentItem()->okRename(currentItem()->renameCol);
+ }
+ }
+ if (d->sortcolumn != Unsorted) {
+ int lcol = d->h->mapToLogical(column);
+ setSorting(lcol, d->sortcolumn == lcol ? !d->ascending : true);
+ }
+}
+
+/*!
+ \internal
+ Handles renaming when sections are being swapped by the user.
+*/
+
+void Q3ListView::handleIndexChange()
+{
+ if (isRenaming()) {
+ if (d->defRenameAction == Q3ListView::Reject) {
+ currentItem()->cancelRename(currentItem()->renameCol);
+ } else {
+ currentItem()->okRename(currentItem()->renameCol);
+ }
+ }
+ triggerUpdate();
+}
+
+/*!
+ Returns the column by which the list view is sorted, or -1 if
+ sorting is disabled.
+
+ \sa sortOrder()
+*/
+
+int Q3ListView::sortColumn() const
+{
+ return d->sortcolumn;
+}
+
+/*!
+ Sets the sorting column for the list view.
+
+ If \a column is -1, sorting is disabled and the user cannot sort
+ columns by clicking on the column headers. If \a column is larger
+ than the number of columns the user must click on a column header
+ to sort the list view.
+
+ \sa setSorting()
+*/
+void Q3ListView::setSortColumn(int column)
+{
+ setSorting(column, d->ascending);
+}
+
+/*!
+ Returns the sorting order of the list view items.
+
+ \sa sortColumn()
+*/
+Qt::SortOrder Q3ListView::sortOrder() const
+{
+ if (d->ascending)
+ return Qt::AscendingOrder;
+ return Qt::DescendingOrder;
+}
+
+/*!
+ Sets the sort order for the items in the list view to \a order.
+
+ \sa setSorting()
+*/
+void Q3ListView::setSortOrder(Qt::SortOrder order)
+{
+ setSorting(d->sortcolumn, order == Qt::AscendingOrder ? true : false);
+}
+
+/*!
+ Sorts the list view using the last sorting configuration (sort
+ column and ascending/descending).
+*/
+
+void Q3ListView::sort()
+{
+ if (d->r)
+ d->r->sort();
+}
+
+/*!
+ \property Q3ListView::itemMargin
+ \brief the advisory item margin that list items may use
+
+ The item margin defaults to one pixel and is the margin between
+ the item's edges and the area where it draws its contents.
+ Q3ListViewItem::paintFocus() draws in the margin.
+
+ \sa Q3ListViewItem::paintCell()
+*/
+
+void Q3ListView::setItemMargin(int m)
+{
+ if (d->margin == m)
+ return;
+ d->margin = m;
+ if (isVisible()) {
+ d->drawables.clear();
+ triggerUpdate();
+ }
+}
+
+int Q3ListView::itemMargin() const
+{
+ return d->margin;
+}
+
+
+/*!
+ \fn void Q3ListView::rightButtonClicked(Q3ListViewItem *item,
+ const QPoint& point, int column)
+
+ This signal is emitted when the right button is clicked. The \a
+ item is the Q3ListViewItem the button was clicked on (which could
+ be 0 if it wasn't clicked on an item). The \a point where the
+ click occurred is given in global coordinates. If an item was
+ clicked on, \a column is the column within the item that was
+ clicked; otherwise \a column is -1.
+*/
+
+
+/*!
+ \fn void Q3ListView::rightButtonPressed (Q3ListViewItem *item,
+ const QPoint &point, int column)
+
+ This signal is emitted when the right button is pressed. The \a
+ item is the Q3ListViewItem the button was pressed on (which could
+ be 0 if it wasn't pressed on an item). The \a point where the
+ press occurred is given in global coordinates. If an item was
+ pressed on, \a column is the column within the item that was
+ pressed; otherwise \a column is -1.
+*/
+
+/*!
+ \fn void Q3ListView::contextMenuRequested(Q3ListViewItem *item, const QPoint & pos, int col)
+
+ This signal is emitted when the user invokes a context menu with
+ the right mouse button or with special system keys. If the
+ keyboard was used \a item is the current item; if the mouse was
+ used, \a item is the item under the mouse pointer or 0 if there is
+ no item under the mouse pointer. If no item is clicked, the column
+ index emitted is -1.
+
+ \a pos is the position for the context menu in the global
+ coordinate system.
+
+ \a col is the column on which the user pressed, or -1 if the
+ signal was triggered by a key event.
+*/
+
+/*!
+ \reimp
+*/
+void Q3ListView::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange) {
+ reconfigureItems();
+ } else if(ev->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow() && d->scrollTimer)
+ d->scrollTimer->stop();
+ if (!palette().isEqual(QPalette::Active, QPalette::Inactive))
+ viewport()->update();
+ }
+ Q3ScrollView::changeEvent(ev);
+
+ if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange
+ || ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange)
+ reconfigureItems();
+}
+
+/*!
+ Ensures that setup() is called for all currently visible items,
+ and that it will be called for currently invisible items as soon
+ as their parents are opened.
+
+ (A visible item, here, is an item whose parents are all open. The
+ item may happen to be off-screen.)
+
+ \sa Q3ListViewItem::setup()
+*/
+
+void Q3ListView::reconfigureItems()
+{
+ d->fontMetricsHeight = fontMetrics().height();
+ d->minLeftBearing = fontMetrics().minLeftBearing();
+ d->minRightBearing = fontMetrics().minRightBearing();
+ d->ellipsisWidth = fontMetrics().width(QLatin1String("...")) * 2;
+ d->r->setOpen(false);
+ d->r->configured = false;
+ d->r->setOpen(true);
+}
+
+/*!
+ Ensures that the width mode of column \a c is updated according to
+ the width of \a item.
+*/
+
+void Q3ListView::widthChanged(const Q3ListViewItem* item, int c)
+{
+ if (c >= d->h->count())
+ return;
+
+
+ QFontMetrics fm = fontMetrics();
+ int col = c < 0 ? 0 : c;
+ while (col == c || (c < 0 && col < d->h->count())) {
+ if (d->column[col].wmode == Maximum) {
+ int w = item->width(fm, this, col);
+ if (showSortIndicator()) {
+ int tw = d->h->sectionSizeHint( col, fm ).width();
+ tw += 40; //add space for the sort indicator
+ w = qMax(w, tw);
+ }
+ if (col == 0) {
+ int indent = treeStepSize() * item->depth();
+ if (rootIsDecorated())
+ indent += treeStepSize();
+ w += indent;
+ }
+ if (w > columnWidth(col) && !d->h->isStretchEnabled() && !d->h->isStretchEnabled(col)) {
+ d->updateHeader = true;
+ setColumnWidth(col, w);
+ }
+ }
+ col++;
+ }
+}
+
+/*!
+ \property Q3ListView::allColumnsShowFocus
+ \brief whether items should show keyboard focus using all columns
+
+ If this property is true all columns will show focus and selection
+ states, otherwise only column 0 will show focus.
+
+ The default is false.
+
+ Setting this to true if it's not necessary may cause noticeable
+ flicker.
+*/
+
+void Q3ListView::setAllColumnsShowFocus(bool enable)
+{
+ d->allColumnsShowFocus = enable;
+}
+
+bool Q3ListView::allColumnsShowFocus() const
+{
+ return d->allColumnsShowFocus;
+}
+
+
+/*!
+ Returns the first item in this Q3ListView. Returns 0 if there is no
+ first item.
+
+ A list view's items can be traversed using firstChild()
+ and nextSibling() or using a Q3ListViewItemIterator.
+
+ \sa itemAt() Q3ListViewItem::itemBelow() Q3ListViewItem::itemAbove()
+*/
+
+Q3ListViewItem * Q3ListView::firstChild() const
+{
+ if (!d->r)
+ return 0;
+
+ d->r->enforceSortOrder();
+ return d->r->childItem;
+}
+
+/*!
+ Returns the last item in the list view tree. Returns 0 if there
+ are no items in the Q3ListView.
+
+ This function is slow because it traverses the entire tree to find
+ the last item.
+*/
+
+Q3ListViewItem* Q3ListView::lastItem() const
+{
+ Q3ListViewItem* item = firstChild();
+ if (item) {
+ while (item->nextSibling() || item->firstChild()) {
+ if (item->nextSibling())
+ item = item->nextSibling();
+ else
+ item = item->firstChild();
+ }
+ }
+ return item;
+}
+
+/*!
+ Repaints this item on the screen if it is currently visible.
+*/
+
+void Q3ListViewItem::repaint() const
+{
+ Q3ListView *lv = listView();
+ if (lv)
+ lv->repaintItem(this);
+}
+
+
+/*!
+ Repaints \a item on the screen if \a item is currently visible.
+ Takes care to avoid multiple repaints.
+*/
+
+void Q3ListView::repaintItem(const Q3ListViewItem * item) const
+{
+ if (!item)
+ return;
+ d->dirtyItemTimer->start(0, true);
+ d->dirtyItems.append(item);
+}
+
+
+struct Q3CheckListItemPrivate
+{
+ Q3CheckListItemPrivate():
+ exclusive(0),
+ currentState(Q3CheckListItem::Off),
+ tristate(false) {}
+
+ Q3CheckListItem *exclusive;
+ Q3CheckListItem::ToggleState currentState;
+ QHash<Q3CheckListItem *, Q3CheckListItem::ToggleState> statesDict;
+ bool tristate;
+};
+
+
+/*!
+ \class Q3CheckListItem
+ \brief The Q3CheckListItem class provides checkable list view items.
+
+ \compat
+
+ Q3CheckListItems are used in \l{Q3ListView}s to provide
+ \l{Q3ListViewItem}s that are checkboxes, radio buttons or
+ controllers.
+
+ Checkbox and controller check list items may be inserted at any
+ level in a list view. Radio button check list items must be
+ children of a controller check list item.
+
+ The item can be checked or unchecked with setOn(). Its type can be
+ retrieved with type() and its text retrieved with text().
+
+ \img qlistviewitems.png List View Items
+
+ \sa Q3ListViewItem Q3ListView
+*/
+
+/*!
+ \enum Q3CheckListItem::Type
+
+ This enum type specifies a Q3CheckListItem's type:
+
+ \value RadioButton
+ \value CheckBox
+ \value RadioButtonController
+ \value CheckBoxController
+ \omitvalue Controller
+*/
+
+/*!
+ \enum Q3CheckListItem::ToggleState
+
+ This enum specifies a Q3CheckListItem's toggle state.
+
+ \value Off
+ \value NoChange
+ \value On
+*/
+
+
+/*!
+ Constructs a checkable item with parent \a parent, text \a text
+ and of type \a tt. Note that a \c RadioButton must be the child of a
+ \c RadioButtonController, otherwise it will not toggle.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3CheckListItem *parent, const QString &text,
+ Type tt)
+ : Q3ListViewItem(parent, text, QString())
+{
+ myType = tt;
+ init();
+ if (myType == RadioButton) {
+ if (parent->type() != RadioButtonController)
+ qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be "
+ "child of a controller");
+ else
+ d->exclusive = parent;
+ }
+}
+
+/*!
+ Constructs a checkable item with parent \a parent, which is after
+ \a after in the parent's list of children, and with text \a text
+ and of type \a tt. Note that a \c RadioButton must be the child of
+ a \c RadioButtonController, otherwise it will not toggle.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3CheckListItem *parent, Q3ListViewItem *after,
+ const QString &text, Type tt)
+ : Q3ListViewItem(parent, after, text)
+{
+ myType = tt;
+ init();
+ if (myType == RadioButton) {
+ if (parent->type() != RadioButtonController)
+ qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be "
+ "child of a controller");
+ else
+ d->exclusive = parent;
+ }
+}
+
+/*!
+ Constructs a checkable item with parent \a parent, text \a text
+ and of type \a tt. Note that this item must \e not be a \c
+ RadioButton. Radio buttons must be children of a \c
+ RadioButtonController.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3ListViewItem *parent, const QString &text,
+ Type tt)
+ : Q3ListViewItem(parent, text, QString())
+{
+ myType = tt;
+ if (myType == RadioButton) {
+ qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be "
+ "child of a Q3CheckListItem");
+ }
+ init();
+}
+
+/*!
+ Constructs a checkable item with parent \a parent, which is after
+ \a after in the parent's list of children, with text \a text and
+ of type \a tt. Note that this item must \e not be a \c
+ RadioButton. Radio buttons must be children of a \c
+ RadioButtonController.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3ListViewItem *parent, Q3ListViewItem *after,
+ const QString &text, Type tt)
+ : Q3ListViewItem(parent, after, text)
+{
+ myType = tt;
+ if (myType == RadioButton) {
+ qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be "
+ "child of a Q3CheckListItem");
+ }
+ init();
+}
+
+
+/*!
+ Constructs a checkable item with parent \a parent, text \a text
+ and of type \a tt. Note that \a tt must \e not be \c RadioButton.
+ Radio buttons must be children of a \c RadioButtonController.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3ListView *parent, const QString &text,
+ Type tt)
+ : Q3ListViewItem(parent, text)
+{
+ myType = tt;
+ if (tt == RadioButton)
+ qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be "
+ "child of a Q3CheckListItem");
+ init();
+}
+
+/*!
+ Constructs a checkable item with parent \a parent, which is after
+ \a after in the parent's list of children, with text \a text and
+ of type \a tt. Note that \a tt must \e not be \c RadioButton.
+ Radio buttons must be children of a \c RadioButtonController.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3ListView *parent, Q3ListViewItem *after,
+ const QString &text, Type tt)
+ : Q3ListViewItem(parent, after, text)
+{
+ myType = tt;
+ if (tt == RadioButton)
+ qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be "
+ "child of a Q3CheckListItem");
+ init();
+}
+
+
+/* \reimp */
+
+int Q3CheckListItem::rtti() const
+{
+ return RTTI;
+}
+
+/*!
+ Constructs a \c RadioButtonController item with parent \a parent,
+ text \a text and pixmap \a p.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3ListView *parent, const QString &text,
+ const QPixmap & p)
+ : Q3ListViewItem(parent, text)
+{
+ myType = RadioButtonController;
+ setPixmap(0, p);
+ init();
+}
+
+/*!
+ Constructs a \c RadioButtonController item with parent \a parent,
+ text \a text and pixmap \a p.
+*/
+Q3CheckListItem::Q3CheckListItem(Q3ListViewItem *parent, const QString &text,
+ const QPixmap & p)
+ : Q3ListViewItem(parent, text)
+{
+ myType = RadioButtonController;
+ setPixmap(0, p);
+ init();
+}
+
+void Q3CheckListItem::init()
+{
+ d = new Q3CheckListItemPrivate();
+ on = false;
+ // CheckBoxControllers by default have tristate set to true
+ if (myType == CheckBoxController)
+ setTristate(true);
+}
+
+/*!
+ Destroys the item, and all its children to any depth, freeing up
+ all allocated resources.
+*/
+Q3CheckListItem::~Q3CheckListItem()
+{
+ if (myType == RadioButton
+ && d->exclusive && d->exclusive->d
+ && d->exclusive->d->exclusive == this)
+ d->exclusive->turnOffChild();
+ d->exclusive = 0; // so the children won't try to access us.
+ delete d;
+ d = 0;
+}
+
+/*!
+ \fn Q3CheckListItem::Type Q3CheckListItem::type() const
+
+ Returns the type of this item.
+*/
+
+/*!
+ \fn bool Q3CheckListItem::isOn() const
+
+ Returns true if the item is toggled on; otherwise returns false.
+*/
+
+/*!
+ Sets tristate to \a b if the \c Type is either a \c CheckBoxController or
+ a \c CheckBox.
+
+ \c CheckBoxControllers are tristate by default.
+
+ \sa state() isTristate()
+*/
+void Q3CheckListItem::setTristate(bool b)
+{
+ if ((myType != CheckBoxController) && (myType != CheckBox)) {
+ qWarning("Q3CheckListItem::setTristate(), has no effect on RadioButton "
+ "or RadioButtonController.");
+ return;
+ }
+ d->tristate = b;
+}
+
+/*!
+ Returns true if the item is tristate; otherwise returns false.
+
+ \sa setTristate()
+*/
+bool Q3CheckListItem::isTristate() const
+{
+ return d->tristate;
+}
+
+/*!
+ Returns the state of the item.
+
+ \sa Q3CheckListItem::ToggleState
+*/
+Q3CheckListItem::ToggleState Q3CheckListItem::state() const
+{
+ if (!isTristate() && internalState() == NoChange)
+ return Off;
+ else
+ return d->currentState;
+}
+
+/*
+ Same as the public state() except this one does not mask NoChange into Off
+ when tristate is disabled.
+*/
+Q3CheckListItem::ToggleState Q3CheckListItem::internalState() const
+{
+ return d->currentState;
+}
+
+
+
+
+/*!
+ Sets the toggle state of the checklistitem to \a s. \a s can be
+ \c Off, \c NoChange or \c On.
+
+ Tristate can only be enabled for \c CheckBox or \c CheckBoxController,
+ therefore the \c NoChange only applies to them.
+
+ Setting the state to \c On or \c Off on a \c CheckBoxController
+ will recursivly set the states of its children to the same state.
+
+ Setting the state to \c NoChange on a \c CheckBoxController will
+ make it recursivly recall the previous stored state of its
+ children. If there was no previous stored state the children are
+ all set to \c On.
+*/
+void Q3CheckListItem::setState(ToggleState s)
+{
+ if (myType == CheckBoxController && state() == NoChange)
+ updateStoredState(this);
+ setState(s, true, true);
+}
+
+/*
+ Sets the toggle state of the checklistitems. \a update tells if the
+ controller / parent controller should be aware of these changes, \a store
+ tells if the parent should store its children if certain conditions arise
+*/
+void Q3CheckListItem::setState(ToggleState s, bool update, bool store)
+{
+
+ if (s == internalState())
+ return;
+
+ if (myType == CheckBox) {
+ setCurrentState(s);
+ stateChange(state());
+ if (update && parent() && parent()->rtti() == 1
+ && ((Q3CheckListItem*)parent())->type() == CheckBoxController)
+ ((Q3CheckListItem*)parent())->updateController(update, store);
+ } else if (myType == CheckBoxController) {
+ if (s == NoChange && childCount()) {
+ restoreState(this);
+ } else {
+ Q3ListViewItem *item = firstChild();
+ int childCount = 0;
+ while(item) {
+ if (item->rtti() == 1 &&
+ (((Q3CheckListItem*)item)->type() == CheckBox ||
+ ((Q3CheckListItem*)item)->type() == CheckBoxController)) {
+ Q3CheckListItem *checkItem = (Q3CheckListItem*)item;
+ checkItem->setState(s, false, false);
+ childCount++;
+ }
+ item = item->nextSibling();
+ }
+ if (update) {
+ if (childCount > 0) {
+ ToggleState oldState = internalState();
+ updateController(false, false);
+ if (oldState != internalState() &&
+ parent() && parent()->rtti() == 1 &&
+ ((Q3CheckListItem*)parent())->type() == CheckBoxController)
+ ((Q3CheckListItem*)parent())->updateController(update, store);
+
+ updateController(update, store);
+ } else {
+ // if there are no children we simply set the CheckBoxController and update its parent
+ setCurrentState(s);
+ stateChange(state());
+ if (parent() && parent()->rtti() == 1
+ && ((Q3CheckListItem*)parent())->type() == CheckBoxController)
+ ((Q3CheckListItem*)parent())->updateController(update, store);
+ }
+ } else {
+ setCurrentState(s);
+ stateChange(state());
+ }
+
+ }
+ } else if (myType == RadioButton) {
+ if (s == On) {
+ if (d->exclusive && d->exclusive->d->exclusive != this)
+ d->exclusive->turnOffChild();
+ setCurrentState(s);
+ if (d->exclusive)
+ d->exclusive->d->exclusive = this;
+ } else {
+ if (d->exclusive && d->exclusive->d->exclusive == this)
+ d->exclusive->d->exclusive = 0;
+ setCurrentState(Off);
+ }
+ stateChange(state());
+ }
+ repaint();
+}
+
+/*
+ this function is needed because we need to update "on" every time we
+ update d->currentState. In order to retain binary compatibility the
+ inline function isOn() needs the "on" bool ### should be changed in
+ ver 4
+*/
+void Q3CheckListItem::setCurrentState(ToggleState s)
+{
+ ToggleState old = d->currentState;
+ d->currentState = s;
+ if (d->currentState == On)
+ on = true;
+ else
+ on = false;
+
+#ifndef QT_NO_ACCESSIBILITY
+ if (old != d->currentState && listView())
+ QAccessible::updateAccessibility(listView()->viewport(), indexOfItem(this), QAccessible::StateChanged);
+#else
+ Q_UNUSED(old);
+#endif
+}
+
+
+
+/*
+ updates the internally stored state of this item for the parent (key)
+*/
+void Q3CheckListItem::setStoredState(ToggleState newState, Q3CheckListItem *key)
+{
+ if (myType == CheckBox || myType == CheckBoxController)
+ d->statesDict[key] = newState;
+}
+
+/*
+ Returns the stored state for this item for the given key.
+ If the key is not found it returns Off.
+*/
+Q3CheckListItem::ToggleState Q3CheckListItem::storedState(Q3CheckListItem *key) const
+{
+ QHash<Q3CheckListItem *, Q3CheckListItem::ToggleState>::Iterator it = d->statesDict.find(key);
+ if (it != d->statesDict.end())
+ return it.value();
+ else
+ return Off;
+}
+
+
+/*!
+ \fn QString Q3CheckListItem::text() const
+
+ Returns the item's text.
+*/
+
+
+/*!
+ If this is a \c RadioButtonController that has \c RadioButton
+ children, turn off the child that is on.
+*/
+void Q3CheckListItem::turnOffChild()
+{
+ if (myType == RadioButtonController && d->exclusive)
+ d->exclusive->setOn(false);
+}
+
+/*!
+ Toggle check box or set radio button to on.
+*/
+void Q3CheckListItem::activate()
+{
+ Q3ListView * lv = listView();
+
+ if ((lv && !lv->isEnabled()) || !isEnabled())
+ return;
+
+ QPoint pos;
+ int boxsize = lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv);
+ if (activatedPos(pos)) {
+ bool parentControl = false;
+ if (parent() && parent()->rtti() == 1 &&
+ ((Q3CheckListItem*) parent())->type() == RadioButtonController)
+ parentControl = true;
+
+ int x = parentControl ? 0 : 3;
+ int align = lv->columnAlignment(0);
+ int marg = lv->itemMargin();
+ int y = 0;
+
+ if (align & Qt::AlignVCenter)
+ y = ((height() - boxsize) / 2) + marg;
+ else
+ y = (lv->fontMetrics().height() + 2 + marg - boxsize) / 2;
+
+ QRect r(x, y, boxsize-3, boxsize-3);
+ // columns might have been swapped
+ r.moveBy(lv->header()->sectionPos(0), 0);
+ if (!r.contains(pos))
+ return;
+ }
+ if ((myType == CheckBox) || (myType == CheckBoxController)) {
+ lv->d->startEdit = FALSE;
+ switch (internalState()) {
+ case On:
+ setState(Off);
+ break;
+ case Off:
+ if ( (!isTristate() && myType == CheckBox) ||
+ (myType == CheckBoxController && !childCount()) ) {
+ setState(On);
+ } else {
+ setState(NoChange);
+ if (myType == CheckBoxController && internalState() != NoChange)
+ setState(On);
+ }
+ break;
+ case NoChange:
+ setState(On);
+ break;
+ }
+ ignoreDoubleClick();
+ } else if (myType == RadioButton) {
+ setOn(true);
+ ignoreDoubleClick();
+ }
+}
+
+/*!
+ Sets the button on if \a b is true, otherwise sets it off.
+ Maintains radio button exclusivity.
+*/
+void Q3CheckListItem::setOn(bool b )
+{
+ if (b)
+ setState(On , true, true);
+ else
+ setState(Off , true, true);
+}
+
+
+/*!
+ \fn void Q3CheckListItem::stateChange(bool b)
+
+ This virtual function is called when the item changes its state.
+ \a b is true if the state is \c On; otherwise the state is \c Off.
+ \c NoChange (if tristate is enabled and the type is either \c
+ CheckBox or \c CheckBoxController) reports the same as \c Off, so
+ use state() to determine if the state is actually \c Off or \c
+ NoChange.
+*/
+void Q3CheckListItem::stateChange(bool)
+{
+}
+
+/*
+ Calls the public virtual function if the state is changed to either On, NoChange or Off.
+ NoChange reports the same as Off - ### should be fixed in ver4
+*/
+void Q3CheckListItem::stateChange(ToggleState s)
+{
+ stateChange(s == On);
+}
+
+/*
+ sets the state of the CheckBox and CheckBoxController back to
+ previous stored state
+*/
+void Q3CheckListItem::restoreState(Q3CheckListItem *key, int depth)
+{
+ switch (type()) {
+ case CheckBox:
+ setCurrentState(storedState(key));
+ stateChange(state());
+ repaint();
+ break;
+ case CheckBoxController: {
+ Q3ListViewItem *item = firstChild();
+ int childCount = 0;
+ while (item) {
+ // recursively calling restoreState for children of type CheckBox and CheckBoxController
+ if (item->rtti() == 1 &&
+ (((Q3CheckListItem*)item)->type() == CheckBox ||
+ ((Q3CheckListItem*)item)->type() == CheckBoxController)) {
+ ((Q3CheckListItem*)item)->restoreState(key , depth+1);
+ childCount++;
+ }
+ item = item->nextSibling();
+ }
+ if (childCount > 0) {
+ if (depth == 0)
+ updateController(true);
+ else
+ updateController(false);
+ } else {
+ // if there are no children we retrieve the CheckBoxController state directly.
+ setState(storedState(key), true, false);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ Checks the childrens state and updates the controllers state
+ if necessary. If the controllers state change, then his parent again is
+ called to update itself.
+*/
+void Q3CheckListItem::updateController(bool update , bool store)
+{
+ if (myType != CheckBoxController)
+ return;
+
+ Q3CheckListItem *controller = 0;
+ // checks if this CheckBoxController has another CheckBoxController as parent
+ if (parent() && parent()->rtti() == 1
+ && ((Q3CheckListItem*)parent())->type() == CheckBoxController)
+ controller = (Q3CheckListItem*)parent();
+
+ ToggleState theState = Off;
+ bool first = true;
+ Q3ListViewItem *item = firstChild();
+ while(item && theState != NoChange) {
+ if (item->rtti() == 1 &&
+ (((Q3CheckListItem*)item)->type() == CheckBox ||
+ ((Q3CheckListItem*)item)->type() == CheckBoxController)) {
+ Q3CheckListItem *checkItem = (Q3CheckListItem*)item;
+ if (first) {
+ theState = checkItem->internalState();
+ first = false;
+ } else {
+ if (checkItem->internalState() == NoChange ||
+ theState != checkItem->internalState())
+ theState = NoChange;
+ else
+ theState = checkItem->internalState();
+ }
+ }
+ item = item->nextSibling();
+ }
+ if (internalState() != theState) {
+ setCurrentState(theState);
+ if (store && (internalState() == On || internalState() == Off))
+ updateStoredState(this);
+ stateChange(state());
+ if (update && controller) {
+ controller->updateController(update, store);
+ }
+ repaint();
+ }
+}
+
+
+/*
+ Makes all the children CheckBoxes update their storedState
+*/
+void Q3CheckListItem::updateStoredState(Q3CheckListItem *key)
+{
+ if (myType != CheckBoxController)
+ return;
+
+ Q3ListViewItem *item = firstChild();
+ while(item) {
+ if (item->rtti() == 1) {
+ Q3CheckListItem *checkItem = (Q3CheckListItem*)item;
+ if (checkItem->type() == CheckBox)
+ checkItem->setStoredState(checkItem->internalState(), key);
+ else if (checkItem->type() == CheckBoxController)
+ checkItem->updateStoredState(key);
+ }
+ item = item->nextSibling();
+ }
+ // this state is only needed if the CheckBoxController has no CheckBox / CheckBoxController children.
+ setStoredState(internalState() , key);
+}
+
+
+/*!
+ \reimp
+*/
+void Q3CheckListItem::setup()
+{
+ Q3ListViewItem::setup();
+ int h = height();
+ Q3ListView *lv = listView();
+ if (lv)
+ h = qMax(lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv),
+ h);
+ h = qMax(h, QApplication::globalStrut().height());
+ setHeight(h);
+}
+
+/*!
+ \reimp
+*/
+
+int Q3CheckListItem::width(const QFontMetrics& fm, const Q3ListView* lv, int column) const
+{
+ int r = Q3ListViewItem::width(fm, lv, column);
+ if (column == 0) {
+ r += lv->itemMargin();
+ if (myType == RadioButtonController && pixmap(0)) {
+ // r += 0;
+ } else {
+ r += lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv) + 4;
+ }
+ }
+ return qMax(r, QApplication::globalStrut().width());
+}
+
+/*!
+ Paints the item using the painter \a p and the color group \a cg.
+ The item is in column \a column, has width \a width and has
+ alignment \a align. (See \l Qt::Alignment for valid alignments.)
+*/
+void Q3CheckListItem::paintCell(QPainter * p, const QColorGroup & cg,
+ int column, int width, int align)
+{
+ if (!p)
+ return;
+
+ Q3ListView *lv = listView();
+ if (!lv)
+ return;
+
+ const QPalette::ColorRole crole = lv->backgroundRole();
+ if (cg.brush(crole) != lv->palette().brush(cg.currentColorGroup(), crole))
+ p->fillRect(0, 0, width, height(), cg.brush(crole));
+ else
+ lv->paintEmptyArea(p, QRect(0, 0, width, height()));
+
+ if (column != 0) {
+ // The rest is text, or for subclasses to change.
+ Q3ListViewItem::paintCell(p, cg, column, width, align);
+ return;
+ }
+
+ bool parentControl = false;
+ if (parent() && parent()->rtti() == 1 &&
+ ((Q3CheckListItem*) parent())->type() == RadioButtonController)
+ parentControl = true;
+
+ QFontMetrics fm(lv->fontMetrics());
+ int boxsize = lv->style()->pixelMetric(myType == RadioButtonController ? QStyle::PM_CheckListControllerSize :
+ QStyle::PM_CheckListButtonSize, 0, lv);
+ int marg = lv->itemMargin();
+ int r = marg;
+
+ // Draw controller / check box / radio button ---------------------
+ QStyle::State styleflags = QStyle::State_None;
+ if (internalState() == On) {
+ styleflags |= QStyle::State_On;
+ } else if (internalState() == NoChange) {
+ if (myType == CheckBoxController && !isTristate())
+ styleflags |= QStyle::State_Off;
+ else
+ styleflags |= QStyle::State_NoChange;
+ } else {
+ styleflags |= QStyle::State_Off;
+ }
+ if (isSelected())
+ styleflags |= QStyle::State_Selected;
+ if (isEnabled() && lv->isEnabled())
+ styleflags |= QStyle::State_Enabled;
+ if (lv->window()->isActiveWindow())
+ styleflags |= QStyle::State_Active;
+
+ if (myType == RadioButtonController) {
+ int x = 0;
+ if(!parentControl)
+ x += 3;
+ if (!pixmap(0)) {
+ QStyleOptionQ3ListView opt = getStyleOption(lv, this);
+ opt.rect.setRect(x, 0, boxsize, fm.height() + 2 + marg);
+ opt.palette = cg;
+ opt.state = styleflags;
+ lv->style()->drawPrimitive(QStyle::PE_Q3CheckListController, &opt, p, lv);
+ r += boxsize + 4;
+ }
+ } else {
+ Q_ASSERT(lv); //###
+ int x = 0;
+ int y = 0;
+ if (!parentControl)
+ x += 3;
+ if (align & Qt::AlignVCenter)
+ y = ((height() - boxsize) / 2) + marg;
+ else
+ y = (fm.height() + 2 + marg - boxsize) / 2;
+
+ QStyleOptionQ3ListView opt = getStyleOption(lv, this);
+ opt.rect.setRect(x, y, boxsize, fm.height() + 2 + marg);
+ opt.palette = cg;
+ opt.state = styleflags;
+ lv->style()->drawPrimitive((myType == CheckBox || myType == CheckBoxController)
+ ? QStyle::PE_Q3CheckListIndicator
+ : QStyle::PE_Q3CheckListExclusiveIndicator, &opt, p, lv);
+ r += boxsize + 4;
+ }
+
+ // Draw text ----------------------------------------------------
+ p->translate(r, 0);
+ p->setPen(QPen(cg.text()));
+ Q3ListViewItem::paintCell(p, cg, column, width - r, align);
+}
+
+/*!
+ Draws the focus rectangle \a r using the color group \a cg on the
+ painter \a p.
+*/
+void Q3CheckListItem::paintFocus(QPainter *p, const QColorGroup & cg,
+ const QRect & r)
+{
+ bool intersect = true;
+ Q3ListView *lv = listView();
+ if (lv && lv->header()->mapToActual(0) != 0) {
+ int xdepth = lv->treeStepSize() * (depth() + (lv->rootIsDecorated() ? 1 : 0)) + lv->itemMargin();
+ int p = lv->header()->cellPos(lv->header()->mapToActual(0));
+ xdepth += p;
+ intersect = r.intersects(QRect(p, r.y(), xdepth - p + 1, r.height()));
+ }
+ bool parentControl = false;
+ if (parent() && parent()->rtti() == 1 &&
+ ((Q3CheckListItem*) parent())->type() == RadioButtonController)
+ parentControl = true;
+ if (myType != RadioButtonController && intersect &&
+ (lv->rootIsDecorated() || myType == RadioButton ||
+ (myType == CheckBox && parentControl))) {
+ QRect rect;
+ int boxsize = lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv);
+ if (lv->columnAlignment(0) == Qt::AlignCenter) {
+ QFontMetrics fm(lv->font());
+ int bx = (lv->columnWidth(0) - (boxsize + fm.width(text())))/2 + boxsize;
+ if (bx < 0) bx = 0;
+ rect.setRect(r.x() + bx + 5, r.y(), r.width() - bx - 5,
+ r.height());
+ } else
+ rect.setRect(r.x() + boxsize + 5, r.y(), r.width() - boxsize - 5,
+ r.height());
+ Q3ListViewItem::paintFocus(p, cg, rect);
+ } else {
+ Q3ListViewItem::paintFocus(p, cg, r);
+ }
+}
+
+/*!
+ \reimp
+*/
+QSize Q3ListView::sizeHint() const
+{
+ if (cachedSizeHint().isValid())
+ return cachedSizeHint();
+
+ ensurePolished();
+
+ if (!isVisible() && d->drawables.isEmpty())
+ // force the column widths to sanity, if possible
+ buildDrawableList();
+
+ QSize s(d->h->sizeHint());
+ if (verticalScrollBar()->isVisible())
+ s.setWidth(s.width() + style()->pixelMetric(QStyle::PM_ScrollBarExtent));
+ s += QSize(frameWidth()*2,frameWidth()*2);
+ Q3ListViewItem * l = d->r;
+ while(l && !l->height())
+ l = l->childItem ? l->childItem : l->siblingItem;
+
+ if (l && l->height())
+ s.setHeight(s.height() + 10 * l->height());
+ else
+ s.setHeight(s.height() + 140);
+
+ if (s.width() > s.height() * 3)
+ s.setHeight(s.width() / 3);
+ else if (s.width() *3 < s.height())
+ s.setHeight(s.width() * 3);
+
+ setCachedSizeHint(s);
+
+ return s;
+}
+
+
+/*!
+ \reimp
+*/
+
+QSize Q3ListView::minimumSizeHint() const
+{
+ return Q3ScrollView::minimumSizeHint();
+}
+
+
+/*!
+ Sets \a item to be open if \a open is true and \a item is
+ expandable, and to be closed if \a open is false. Repaints
+ accordingly.
+
+ \sa Q3ListViewItem::setOpen() Q3ListViewItem::setExpandable()
+*/
+
+void Q3ListView::setOpen(Q3ListViewItem * item, bool open)
+{
+ if (!item ||
+ item->isOpen() == open ||
+ (open && !item->childCount() && !item->isExpandable()))
+ return;
+
+ Q3ListViewItem* nextParent = 0;
+ if (open)
+ nextParent = item->itemBelow();
+
+ item->setOpen(open);
+
+ if (open) {
+ Q3ListViewItem* lastChild = item;
+ Q3ListViewItem* tmp;
+ while (true) {
+ tmp = lastChild->itemBelow();
+ if (!tmp || tmp == nextParent)
+ break;
+ lastChild = tmp;
+ }
+ ensureItemVisible(lastChild);
+ ensureItemVisible(item);
+ }
+ buildDrawableList();
+
+ int i = 0;
+ for (; i < d->drawables.size(); ++i) {
+ const Q3ListViewPrivate::DrawableItem &c = d->drawables.at(i);
+ if(c.i == item)
+ break;
+ }
+
+ if (i < d->drawables.size()) {
+ d->dirtyItemTimer->start(0, true);
+ for (; i < d->drawables.size(); ++i) {
+ const Q3ListViewPrivate::DrawableItem &c = d->drawables.at(i);
+ d->dirtyItems.append(c.i);
+ }
+ }
+}
+
+
+/*!
+ Returns true if this list view item has children \e and they are
+ not explicitly hidden; otherwise returns false.
+
+ Identical to \a{item}->isOpen(). Provided for completeness.
+
+ \sa setOpen()
+*/
+
+bool Q3ListView::isOpen(const Q3ListViewItem * item) const
+{
+ return item->isOpen();
+}
+
+
+/*!
+ \property Q3ListView::rootIsDecorated
+ \brief whether the list view shows open/close signs on root items
+
+ Open/close signs are small \bold{+} or \bold{-} symbols in windows
+ style, or arrows in Motif style. The default is false.
+*/
+
+void Q3ListView::setRootIsDecorated(bool enable)
+{
+ if (enable != (bool)d->rootIsExpandable) {
+ d->rootIsExpandable = enable;
+ if (isVisible())
+ triggerUpdate();
+ }
+}
+
+bool Q3ListView::rootIsDecorated() const
+{
+ return d->rootIsExpandable;
+}
+
+
+/*!
+ Ensures that item \a i is visible, scrolling the list view
+ vertically if necessary and opening (expanding) any parent items
+ if this is required to show the item.
+
+ \sa itemRect() Q3ScrollView::ensureVisible()
+*/
+
+void Q3ListView::ensureItemVisible(const Q3ListViewItem * i)
+{
+ if (!i || !i->isVisible())
+ return;
+
+ Q3ListViewItem *parent = i->parent();
+ while (parent) {
+ if (!parent->isOpen())
+ parent->setOpen(true);
+ parent = parent->parent();
+ }
+
+ if (d->r->maybeTotalHeight < 0)
+ updateGeometries();
+ int y = itemPos(i);
+ int h = i->height();
+ if (isVisible() && y + h > contentsY() + visibleHeight())
+ setContentsPos(contentsX(), y - visibleHeight() + h);
+ else if (!isVisible() || y < contentsY())
+ setContentsPos(contentsX(), y);
+}
+
+
+/*!
+ \fn QString Q3CheckListItem::text(int n) const
+
+ \reimp
+*/
+
+/*!
+ Returns the Q3Header object that manages this list view's columns.
+ Please don't modify the header behind the list view's back.
+
+ You may safely call Q3Header::setClickEnabled(),
+ Q3Header::setResizeEnabled(), Q3Header::setMovingEnabled(),
+ Q3Header::hide() and all the const Q3Header functions.
+*/
+
+Q3Header * Q3ListView::header() const
+{
+ return d->h;
+}
+
+
+/*!
+ \property Q3ListView::childCount
+ \brief the number of parentless (top-level) Q3ListViewItem objects in this Q3ListView
+
+ Holds the current number of parentless (top-level) Q3ListViewItem
+ objects in this Q3ListView.
+
+ \sa Q3ListViewItem::childCount()
+*/
+
+int Q3ListView::childCount() const
+{
+ if (d->r)
+ return d->r->childCount();
+ return 0;
+}
+
+
+/*
+ Moves this item to just after \a olderSibling. \a olderSibling and
+ this object must have the same parent.
+
+ If you need to move an item in the hierarchy use takeItem() and
+ insertItem().
+*/
+
+void Q3ListViewItem::moveToJustAfter(Q3ListViewItem * olderSibling)
+{
+ if (parentItem && olderSibling &&
+ olderSibling->parentItem == parentItem && olderSibling != this) {
+ if (parentItem->childItem == this) {
+ parentItem->childItem = siblingItem;
+ } else {
+ Q3ListViewItem * i = parentItem->childItem;
+ while(i && i->siblingItem != this)
+ i = i->siblingItem;
+ if (i)
+ i->siblingItem = siblingItem;
+ }
+ siblingItem = olderSibling->siblingItem;
+ olderSibling->siblingItem = this;
+ parentItem->lsc = Unsorted;
+ }
+}
+
+/*!
+ Move the item to be after item \a after, which must be one of the
+ item's siblings. To move an item in the hierarchy, use takeItem()
+ and insertItem().
+
+ Note that this function will have no effect if sorting is enabled
+ in the list view.
+*/
+
+void Q3ListViewItem::moveItem(Q3ListViewItem *after)
+{
+ if (!after || after == this)
+ return;
+ if (parent() != after->parent()) {
+ if (parentItem)
+ parentItem->takeItem(this);
+ if (after->parentItem) {
+ int tmpLsc = after->parentItem->lsc;
+ after->parentItem->insertItem(this);
+ after->parentItem->lsc = tmpLsc;
+ }
+ }
+ moveToJustAfter(after);
+ Q3ListView *lv = listView();
+ if (lv)
+ lv->triggerUpdate();
+}
+
+/*
+ Recursively sorts items, from the root to this item.
+ (enforceSortOrder() won't work the other way around, as
+ documented.)
+*/
+void Q3ListViewItem::enforceSortOrderBackToRoot()
+{
+ if (parentItem) {
+ parentItem->enforceSortOrderBackToRoot();
+ parentItem->enforceSortOrder();
+ }
+}
+
+/*!
+ \reimp
+*/
+void Q3ListView::showEvent(QShowEvent *)
+{
+ d->drawables.clear();
+ d->dirtyItems.clear();
+ d->dirtyItemTimer->stop();
+ d->fullRepaintOnComlumnChange = true;
+
+ updateGeometries();
+}
+
+
+/*!
+ Returns the y coordinate of this item in the list view's
+ coordinate system. This function is normally much slower than
+ Q3ListView::itemAt(), but it works for all items whereas
+ Q3ListView::itemAt() normally only works for items on the screen.
+
+ \sa Q3ListView::itemAt() Q3ListView::itemRect() Q3ListView::itemPos()
+*/
+
+int Q3ListViewItem::itemPos() const
+{
+ QStack<Q3ListViewItem *> s;
+ Q3ListViewItem * i = (Q3ListViewItem *)this;
+ while(i) {
+ s.push(i);
+ i = i->parentItem;
+ }
+
+ int a = 0;
+ Q3ListViewItem * p = 0;
+ while(s.count()) {
+ i = s.pop();
+ if (p) {
+ if (!p->configured) {
+ p->configured = true;
+ p->setup(); // ### virtual non-const function called in const
+ }
+ a += p->height();
+ Q3ListViewItem * s = p->firstChild();
+ while(s && s != i) {
+ a += s->totalHeight();
+ s = s->nextSibling();
+ }
+ }
+ p = i;
+ }
+ return a;
+}
+
+
+/*!
+ \fn void Q3ListView::removeItem(Q3ListViewItem *item)
+
+ Removes the given \a item. Use takeItem() instead.
+*/
+
+/*!
+ Removes item \a i from the list view; \a i must be a top-level
+ item. The warnings regarding Q3ListViewItem::takeItem() apply to
+ this function, too.
+
+ \sa insertItem()
+*/
+void Q3ListView::takeItem(Q3ListViewItem * i)
+{
+ if (d->r)
+ d->r->takeItem(i);
+}
+
+
+void Q3ListView::openFocusItem()
+{
+ d->autoopenTimer->stop();
+ if (d->focusItem && !d->focusItem->isOpen()) {
+ d->focusItem->setOpen(true);
+ d->focusItem->repaint();
+ }
+}
+
+static const int autoopenTime = 750;
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*! \reimp */
+
+void Q3ListView::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ d->oldFocusItem = d->focusItem;
+ Q3ListViewItem *i = d->focusItem;
+ d->focusItem = itemAt(contentsToViewport(e->pos()));
+ if (i)
+ i->repaint();
+ if (d->focusItem) {
+ d->autoopenTimer->start(autoopenTime);
+ d->focusItem->dragEntered();
+ d->focusItem->repaint();
+ }
+ e->accept();
+}
+
+/*! \reimp */
+
+void Q3ListView::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ Q3ListViewItem *i = d->focusItem;
+ d->focusItem = itemAt(contentsToViewport(e->pos()));
+ if (i) {
+ if (i != d->focusItem)
+ i->dragLeft();
+ i->repaint();
+ }
+ if (d->focusItem) {
+ if (i != d->focusItem) {
+ d->focusItem->dragEntered();
+ d->autoopenTimer->stop();
+ d->autoopenTimer->start(autoopenTime);
+ }
+ d->focusItem->repaint();
+ } else {
+ d->autoopenTimer->stop();
+ }
+ if ((i && i->dropEnabled() && i->acceptDrop(e)) || acceptDrops())
+ e->accept();
+ else
+ e->ignore();
+}
+
+/*! \reimp */
+
+void Q3ListView::contentsDragLeaveEvent(QDragLeaveEvent *)
+{
+ d->autoopenTimer->stop();
+
+ if (d->focusItem)
+ d->focusItem->dragLeft();
+
+ setCurrentItem(d->oldFocusItem);
+ d->oldFocusItem = 0;
+}
+
+/*! \reimp */
+
+void Q3ListView::contentsDropEvent(QDropEvent *e)
+{
+ d->autoopenTimer->stop();
+
+ setCurrentItem(d->oldFocusItem);
+ Q3ListViewItem *i = itemAt(contentsToViewport(e->pos()));
+ if (i && i->dropEnabled() && i->acceptDrop(e)) {
+ i->dropped(e);
+ e->accept();
+ } else if (acceptDrops()) {
+ emit dropped(e);
+ e->accept();
+ }
+}
+
+/*!
+ If the user presses the mouse on an item and starts moving the
+ mouse, and the item allow dragging (see
+ Q3ListViewItem::setDragEnabled()), this function is called to get a
+ drag object and a drag is started unless dragObject() returns 0.
+
+ By default this function returns 0. You should reimplement it and
+ create a Q3DragObject depending on the selected items.
+*/
+
+Q3DragObject *Q3ListView::dragObject()
+{
+ return 0;
+}
+
+/*!
+ Starts a drag.
+*/
+
+void Q3ListView::startDrag()
+{
+ if (!d->startDragItem)
+ return;
+
+ d->startDragItem = 0;
+ d->buttonDown = false;
+
+ Q3DragObject *drag = dragObject();
+ if (!drag)
+ return;
+
+ drag->drag();
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ \property Q3ListView::defaultRenameAction
+ \brief What action to perform when the editor loses focus during renaming
+
+ If this property is \c Accept, and the user renames an item and
+ the editor loses focus (without the user pressing Enter), the
+ item will still be renamed. If the property's value is \c Reject,
+ the item will not be renamed unless the user presses Enter. The
+ default is \c Reject.
+*/
+
+void Q3ListView::setDefaultRenameAction(RenameAction a)
+{
+ d->defRenameAction = a;
+}
+
+Q3ListView::RenameAction Q3ListView::defaultRenameAction() const
+{
+ return d->defRenameAction;
+}
+
+/*!
+ Returns true if an item is being renamed; otherwise returns false.
+*/
+
+bool Q3ListView::isRenaming() const
+{
+ return currentItem() && currentItem()->renameBox;
+}
+
+/**********************************************************************
+ *
+ * Class Q3ListViewItemIterator
+ *
+ **********************************************************************/
+
+
+/*!
+ \class Q3ListViewItemIterator
+ \brief The Q3ListViewItemIterator class provides an iterator for collections of Q3ListViewItems.
+
+ \compat
+
+ Construct an instance of a Q3ListViewItemIterator, with either a
+ Q3ListView* or a Q3ListViewItem* as argument, to operate on the tree
+ of Q3ListViewItems, starting from the argument.
+
+ A Q3ListViewItemIterator iterates over all the items from its
+ starting point. This means that it always makes the first child of
+ the current item the new current item. If there is no child, the
+ next sibling becomes the new current item; and if there is no next
+ sibling, the next sibling of the parent becomes current.
+
+ The following example creates a list of all the items that have
+ been selected by the user, storing pointers to the items in a
+ QList:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 6
+
+ An alternative approach is to use an \c IteratorFlag:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 7
+
+ A Q3ListViewItemIterator provides a convenient and easy way to
+ traverse a hierarchical Q3ListView.
+
+ Multiple Q3ListViewItemIterators can operate on the tree of
+ Q3ListViewItems. A Q3ListView knows about all iterators operating on
+ its Q3ListViewItems. So when a Q3ListViewItem gets removed all
+ iterators that point to this item are updated and point to the
+ following item if possible, otherwise to a valid item before the
+ current one or to 0. Note however that deleting the parent item of
+ an item that an iterator points to is not safe.
+
+ \sa Q3ListView, Q3ListViewItem
+*/
+
+/*!
+ \enum Q3ListViewItemIterator::IteratorFlag
+
+ These flags can be passed to a Q3ListViewItemIterator constructor
+ (OR-ed together if more than one is used), so that the iterator
+ will only iterate over items that match the given flags.
+
+ \value Visible
+ \value Invisible
+ \value Selected
+ \value Unselected
+ \value Selectable
+ \value NotSelectable
+ \value DragEnabled
+ \value DragDisabled
+ \value DropEnabled
+ \value DropDisabled
+ \value Expandable
+ \value NotExpandable
+ \value Checked
+ \value NotChecked
+*/
+
+/*!
+ Constructs an empty iterator.
+*/
+
+Q3ListViewItemIterator::Q3ListViewItemIterator()
+ : curr(0), listView(0), flags(0)
+{
+}
+
+/*!
+ Constructs an iterator for the Q3ListView that contains the \a
+ item. The current iterator item is set to point to the \a item.
+*/
+
+Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListViewItem *item)
+ : curr(item), listView(0), flags(0)
+{
+ if (item) {
+ item->enforceSortOrderBackToRoot();
+ listView = item->listView();
+ }
+ if (listView)
+ listView->d->iterators.append(this);
+}
+
+/*!
+ Constructs an iterator for the Q3ListView that contains the \a item
+ using the flags \a iteratorFlags. The current iterator item is set
+ to point to \a item or the next matching item if \a item doesn't
+ match the flags.
+
+ \sa Q3ListViewItemIterator::IteratorFlag
+*/
+
+Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListViewItem *item, int iteratorFlags)
+ : curr(item), listView(0), flags(iteratorFlags)
+{
+ // go to next matching item if the current don't match
+ if (curr && !matchesFlags(curr))
+ ++(*this);
+
+ if (curr) {
+ curr->enforceSortOrderBackToRoot();
+ listView = curr->listView();
+ }
+ if (listView)
+ listView->d->iterators.append(this);
+}
+
+
+/*!
+ Constructs an iterator for the same Q3ListView as \a it. The
+ current iterator item is set to point on the current item of \a
+ it.
+*/
+
+Q3ListViewItemIterator::Q3ListViewItemIterator(const Q3ListViewItemIterator& it)
+ : curr(it.curr), listView(it.listView), flags(it.flags)
+{
+ if (listView)
+ listView->d->iterators.append(this);
+}
+
+/*!
+ Constructs an iterator for the Q3ListView \a lv. The current
+ iterator item is set to point on the first child (Q3ListViewItem)
+ of \a lv.
+*/
+
+Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListView *lv)
+ : curr(lv->firstChild()), listView(lv), flags(0)
+{
+ if (listView)
+ listView->d->iterators.append(this);
+}
+
+/*!
+ Constructs an iterator for the Q3ListView \a lv with the flags \a
+ iteratorFlags. The current iterator item is set to point on the
+ first child (Q3ListViewItem) of \a lv that matches the flags.
+
+ \sa Q3ListViewItemIterator::IteratorFlag
+*/
+
+Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListView *lv, int iteratorFlags)
+ : curr (lv->firstChild()), listView(lv), flags(iteratorFlags)
+{
+ if (listView)
+ listView->d->iterators.append(this);
+ if (!matchesFlags(curr))
+ ++(*this);
+}
+
+
+
+/*!
+ Assignment. Makes a copy of \a it and returns a reference to its
+ iterator.
+*/
+
+Q3ListViewItemIterator &Q3ListViewItemIterator::operator=(const Q3ListViewItemIterator &it)
+{
+ if (listView)
+ listView->d->iterators.removeAll(this);
+
+ listView = it.listView;
+ curr = it.curr;
+ flags = it.flags;
+ if (listView)
+ listView->d->iterators.append(this);
+
+ // go to next matching item if the current don't match
+ if (curr && !matchesFlags(curr))
+ ++(*this);
+
+ return *this;
+}
+
+/*!
+ Destroys the iterator.
+*/
+
+Q3ListViewItemIterator::~Q3ListViewItemIterator()
+{
+ if (listView)
+ listView->d->iterators.removeAll(this);
+}
+
+/*!
+ Prefix ++. Makes the next item the new current item and returns
+ it. Returns 0 if the current item is the last item or the
+ Q3ListView is 0.
+*/
+
+Q3ListViewItemIterator &Q3ListViewItemIterator::operator++()
+{
+ if (!curr)
+ return *this;
+
+ Q3ListViewItem *item = curr->firstChild();
+ if (!item) {
+ while ((item = curr->nextSibling()) == 0 ) {
+ curr = curr->parent();
+ if (curr == 0)
+ break;
+ }
+ }
+ curr = item;
+ // if the next one doesn't match the flags we try one more ahead
+ if (curr && !matchesFlags(curr))
+ ++(*this);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Postfix ++. Makes the next item the new current item and returns
+ the item that \e was the current item.
+*/
+
+const Q3ListViewItemIterator Q3ListViewItemIterator::operator++(int)
+{
+ Q3ListViewItemIterator oldValue = *this;
+ ++(*this);
+ return oldValue;
+}
+
+/*!
+ Sets the current item to the item \a j positions after the current
+ item. If that item is beyond the last item, the current item is
+ set to 0. Returns the current item.
+*/
+
+Q3ListViewItemIterator &Q3ListViewItemIterator::operator+=(int j)
+{
+ while (curr && j--)
+ ++(*this);
+
+ return *this;
+}
+
+/*!
+ Prefix --. Makes the previous item the new current item and
+ returns it. Returns 0 if the current item is the first item or the
+ Q3ListView is 0.
+*/
+
+Q3ListViewItemIterator &Q3ListViewItemIterator::operator--()
+{
+ if (!curr)
+ return *this;
+
+ if (!curr->parent()) {
+ // we are in the first depth
+ if (curr->listView()) {
+ if (curr->listView()->firstChild() != curr) {
+ // go the previous sibling
+ Q3ListViewItem *i = curr->listView()->firstChild();
+ while (i && i->siblingItem != curr)
+ i = i->siblingItem;
+
+ curr = i;
+
+ if (i && i->firstChild()) {
+ // go to the last child of this item
+ Q3ListViewItemIterator it(curr->firstChild());
+ for (; it.current() && it.current()->parent(); ++it)
+ curr = it.current();
+ }
+
+ if (curr && !matchesFlags(curr))
+ --(*this);
+
+ return *this;
+ } else {
+ //we are already the first child of the list view, so it's over
+ curr = 0;
+ return *this;
+ }
+ } else
+ return *this;
+ } else {
+ Q3ListViewItem *parent = curr->parent();
+
+ if (curr != parent->firstChild()) {
+ // go to the previous sibling
+ Q3ListViewItem *i = parent->firstChild();
+ while (i && i->siblingItem != curr)
+ i = i->siblingItem;
+
+ curr = i;
+
+ if (i && i->firstChild()) {
+ // go to the last child of this item
+ Q3ListViewItemIterator it(curr->firstChild());
+ for (; it.current() && it.current()->parent() != parent; ++it)
+ curr = it.current();
+ }
+
+ if (curr && !matchesFlags(curr))
+ --(*this);
+
+ return *this;
+ } else {
+ // make our parent the current item
+ curr = parent;
+
+ if (curr && !matchesFlags(curr))
+ --(*this);
+
+ return *this;
+ }
+ }
+}
+
+/*!
+ \overload
+
+ Postfix --. Makes the previous item the new current item and
+ returns the item that \e was the current item.
+*/
+
+const Q3ListViewItemIterator Q3ListViewItemIterator::operator--(int)
+{
+ Q3ListViewItemIterator oldValue = *this;
+ --(*this);
+ return oldValue;
+}
+
+/*!
+ Sets the current item to the item \a j positions before the
+ current item. If that item is before the first item, the current
+ item is set to 0. Returns the current item.
+*/
+
+Q3ListViewItemIterator &Q3ListViewItemIterator::operator-=(int j)
+{
+ while (curr && j--)
+ --(*this);
+
+ return *this;
+}
+
+/*!
+ Dereference operator. Returns a reference to the current item. The
+ same as current().
+*/
+
+Q3ListViewItem* Q3ListViewItemIterator::operator*()
+{
+ if (curr != 0 && !matchesFlags(curr))
+ qWarning("Q3ListViewItemIterator::operator*() curr out of sync");
+ return curr;
+}
+
+/*!
+ Returns iterator's current item.
+*/
+
+Q3ListViewItem *Q3ListViewItemIterator::current() const
+{
+ if (curr != 0 && !matchesFlags(curr))
+ qWarning("Q3ListViewItemIterator::current() curr out of sync");
+ return curr;
+}
+
+/*
+ This function is called to notify the iterator that the current
+ item has been deleted, and sets the current item point to another
+ (valid) item or 0.
+*/
+
+void Q3ListViewItemIterator::currentRemoved()
+{
+ if (!curr) return;
+
+ if (curr->parent())
+ curr = curr->parent();
+ else if (curr->nextSibling())
+ curr = curr->nextSibling();
+ else if (listView && listView->firstChild() &&
+ listView->firstChild() != curr)
+ curr = listView->firstChild();
+ else
+ curr = 0;
+}
+
+/*
+ returns true if the item \a item matches all of the flags set for the iterator
+*/
+bool Q3ListViewItemIterator::matchesFlags(const Q3ListViewItem *item) const
+{
+ if (!item)
+ return false;
+
+ if (flags == 0)
+ return true;
+
+ if (flags & Visible && !item->isVisible())
+ return false;
+ if (flags & Invisible && item->isVisible())
+ return false;
+ if (flags & Selected && !item->isSelected())
+ return false;
+ if (flags & Unselected && item->isSelected())
+ return false;
+ if (flags & Selectable && !item->isSelectable())
+ return false;
+ if (flags & NotSelectable && item->isSelectable())
+ return false;
+ if (flags & DragEnabled && !item->dragEnabled())
+ return false;
+ if (flags & DragDisabled && item->dragEnabled())
+ return false;
+ if (flags & DropEnabled && !item->dropEnabled())
+ return false;
+ if (flags & DropDisabled && item->dropEnabled())
+ return false;
+ if (flags & Expandable && !item->isExpandable())
+ return false;
+ if (flags & NotExpandable && item->isExpandable())
+ return false;
+ if (flags & Checked && !isChecked(item))
+ return false;
+ if (flags & NotChecked && isChecked(item))
+ return false;
+
+ return true;
+}
+
+/*
+ we want the iterator to check Q3CheckListItems as well, so we provide this convenience function
+ that checks if the rtti() is 1 which means Q3CheckListItem and if isOn is true, returns false otherwise.
+*/
+bool Q3ListViewItemIterator::isChecked(const Q3ListViewItem *item) const
+{
+ if (item->rtti() == 1)
+ return ((const Q3CheckListItem*)item)->isOn();
+ else return false;
+}
+
+void Q3ListView::handleItemChange(Q3ListViewItem *old, bool shift, bool control)
+{
+ if (d->selectionMode == Single) {
+ // nothing
+ } else if (d->selectionMode == Extended) {
+ if (shift) {
+ selectRange(d->selectAnchor ? d->selectAnchor : old,
+ d->focusItem, false, true, (d->selectAnchor && !control) ? true : false);
+ } else if (!control) {
+ bool block = signalsBlocked();
+ blockSignals(true);
+ selectAll(false);
+ blockSignals(block);
+ setSelected(d->focusItem, true);
+ }
+ } else if (d->selectionMode == Multi) {
+ if (shift)
+ selectRange(old, d->focusItem, true, false);
+ }
+}
+
+void Q3ListView::startRename()
+{
+ if (!currentItem())
+ return;
+ currentItem()->startRename(d->pressedColumn);
+ d->buttonDown = false;
+}
+
+/* unselects items from to, including children, returns true if any items were unselected */
+bool Q3ListView::clearRange(Q3ListViewItem *from, Q3ListViewItem *to, bool includeFirst)
+{
+ if (!from || !to)
+ return false;
+
+ // Swap
+ if (from->itemPos() > to->itemPos()) {
+ Q3ListViewItem *temp = from;
+ from = to;
+ to = temp;
+ }
+
+ // Start on second?
+ if (!includeFirst) {
+ Q3ListViewItem *below = (from == to) ? from : from->itemBelow();
+ if (below)
+ from = below;
+ }
+
+ // Clear items <from, to>
+ bool changed = false;
+
+ Q3ListViewItemIterator it(from);
+ while (it.current()) {
+ if (it.current()->isSelected()) {
+ it.current()->setSelected(false);
+ changed = true;
+ }
+ if (it.current() == to)
+ break;
+ ++it;
+ }
+
+ // NOTE! This function does _not_ emit
+ // any signals about selection changed
+ return changed;
+}
+
+void Q3ListView::selectRange(Q3ListViewItem *from, Q3ListViewItem *to, bool invert, bool includeFirst, bool clearSel)
+{
+ if (!from || !to)
+ return;
+ if (from == to && !includeFirst)
+ return;
+ bool swap = false;
+ if (to == from->itemAbove())
+ swap = true;
+ if (!swap && from != to && from != to->itemAbove()) {
+ Q3ListViewItemIterator it(from);
+ bool found = false;
+ for (; it.current(); ++it) {
+ if (it.current() == to) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ swap = true;
+ }
+ if (swap) {
+ Q3ListViewItem *i = from;
+ from = to;
+ to = i;
+ if (!includeFirst)
+ to = to->itemAbove();
+ } else {
+ if (!includeFirst)
+ from = from->itemBelow();
+ }
+
+ bool changed = false;
+ if (clearSel) {
+ Q3ListViewItemIterator it(firstChild());
+ for (; it.current(); ++it) {
+ if (it.current()->selected) {
+ it.current()->setSelected(false);
+ changed = true;
+ }
+ }
+ it = Q3ListViewItemIterator(to);
+ for (; it.current(); ++it) {
+ if (it.current()->selected) {
+ it.current()->setSelected(false);
+ changed = true;
+ }
+ }
+ }
+
+ for (Q3ListViewItem *i = from; i; i = i->itemBelow()) {
+ if (!invert) {
+ if (!i->selected && i->isSelectable()) {
+ i->setSelected(true);
+ changed = true;
+ }
+ } else {
+ bool sel = !i->selected;
+ if (((bool)i->selected != sel && sel && i->isSelectable()) || !sel) {
+ i->setSelected(sel);
+ changed = true;
+ }
+ }
+ if (i == to)
+ break;
+ }
+ if (changed) {
+ triggerUpdate();
+ emit selectionChanged();
+ }
+}
+
+/* clears selection from anchor to old, selects from anchor to new, does not emit selectionChanged on change */
+bool Q3ListView::selectRange(Q3ListViewItem *newItem, Q3ListViewItem *oldItem, Q3ListViewItem *anchorItem)
+{
+ if (!newItem || !oldItem || !anchorItem)
+ return false;
+
+ int anchorPos = anchorItem ? anchorItem->itemPos() : 0,
+ oldPos = oldItem ? oldItem->itemPos() : 0,
+ newPos = newItem->itemPos();
+ Q3ListViewItem *top=0, *bottom=0;
+ if (anchorPos > newPos) {
+ top = newItem;
+ bottom = anchorItem;
+ } else {
+ top = anchorItem;
+ bottom = newItem;
+ }
+
+ // removes the subControls of the old selection that will no longer be selected
+ bool changed = false;
+ int topPos = top ? top->itemPos() : 0,
+ bottomPos = bottom ? bottom->itemPos() : 0;
+ if (!(oldPos > topPos && oldPos < bottomPos)) {
+ if (oldPos < topPos)
+ changed = clearRange(oldItem, top);
+ else
+ changed = clearRange(bottom, oldItem);
+ }
+
+ // selects the new (not already selected) items
+ Q3ListViewItemIterator lit(top);
+ for (; lit.current(); ++lit) {
+ if ((bool)lit.current()->selected != d->select) {
+ lit.current()->setSelected(d->select);
+ changed = true;
+ }
+ // Include bottom, then break
+ if (lit.current() == bottom)
+ break;
+ }
+
+ return changed;
+}
+
+
+/*!
+ Finds the first list view item in column \a column, that matches
+ \a text and returns the item, or returns 0 of no such item could
+ be found. Pass OR-ed together \l ComparisonFlags values
+ in the \a compare flag, to control how the matching is performed.
+ The default comparison mode is case-sensitive, exact match.
+*/
+
+Q3ListViewItem *Q3ListView::findItem(const QString& text, int column,
+ ComparisonFlags compare) const
+{
+ if (text.isEmpty() && !(compare & ExactMatch))
+ return 0;
+
+ if (compare == Qt::CaseSensitive || compare == 0)
+ compare |= ExactMatch;
+
+ QString itmtxt;
+ QString comtxt = text;
+ if (!(compare & Qt::CaseSensitive))
+ comtxt = comtxt.toLower();
+
+ Q3ListViewItemIterator it(d->focusItem ? d->focusItem : firstChild());
+ Q3ListViewItem *sentinel = 0;
+ Q3ListViewItem *item;
+ Q3ListViewItem *beginsWithItem = 0;
+ Q3ListViewItem *endsWithItem = 0;
+ Q3ListViewItem *containsItem = 0;
+
+ for (int pass = 0; pass < 2; pass++) {
+ while ((item = it.current()) != sentinel) {
+ itmtxt = item->text(column);
+ if (!(compare & CaseSensitive))
+ itmtxt = itmtxt.toLower();
+
+ if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt)
+ return item;
+ if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt))
+ beginsWithItem = containsItem = item;
+ if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt))
+ endsWithItem = containsItem = item;
+ if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt))
+ containsItem = item;
+ ++it;
+ }
+
+ it = Q3ListViewItemIterator(firstChild());
+ sentinel = d->focusItem ? d->focusItem : firstChild();
+ }
+
+ // Obey the priorities
+ if (beginsWithItem)
+ return beginsWithItem;
+ else if (endsWithItem)
+ return endsWithItem;
+ else if (containsItem)
+ return containsItem;
+ return 0;
+}
+
+/*!
+ Hides the column specified at \a column. This is a convenience
+ function that calls setColumnWidth(column, 0).
+
+ Note: The user may still be able to resize the hidden column using
+ the header handles. To prevent this, call setResizeEnabled(false,
+ \a column) on the list views header.
+
+ \sa setColumnWidth()
+*/
+
+void Q3ListView::hideColumn(int column)
+{
+ setColumnWidth(column, 0);
+}
+
+/*! Adjusts the column \a col to its preferred width */
+
+void Q3ListView::adjustColumn(int col)
+{
+ if (col < 0 || col > (int)d->column.count() - 1 || d->h->isStretchEnabled(col))
+ return;
+
+ int oldw = d->h->sectionSize(col);
+
+ int w = d->h->sectionSizeHint(col, fontMetrics()).width();
+ if (d->h->iconSet(col))
+ w += d->h->iconSet(col)->pixmap().width();
+ w = qMax(w, 20);
+ QFontMetrics fm(fontMetrics());
+ Q3ListViewItem* item = firstChild();
+ int rootDepth = rootIsDecorated() ? treeStepSize() : 0;
+ while (item) {
+ int iw = item->width(fm, this, col);
+ if (0 == col)
+ iw += itemMargin() + rootDepth + item->depth()*treeStepSize() - 1;
+ w = qMax(w, iw);
+ item = item->itemBelow();
+ }
+ w = qMax(w, QApplication::globalStrut().width());
+
+ d->h->adjustHeaderSize(oldw - w);
+ if (oldw != w) {
+ d->fullRepaintOnComlumnChange = true;
+ d->h->resizeSection(col, w);
+ emit d->h->sizeChange(col, oldw, w);
+ }
+}
+
+/*!
+ \enum Q3ListView::StringComparisonMode
+
+ This enum type is used to set the string comparison mode when
+ searching for an item. We'll refer to the string being searched
+ as the 'target' string.
+
+ \value CaseSensitive The strings must match case sensitively.
+ \value ExactMatch The target and search strings must match exactly.
+ \value BeginsWith The target string begins with the search string.
+ \value EndsWith The target string ends with the search string.
+ \value Contains The target string contains the search string.
+
+ If you OR these flags together (excluding \c CaseSensitive), the
+ search criteria be applied in the following order: \c ExactMatch,
+ \c BeginsWith, \c EndsWith, \c Contains.
+
+ Matching is case-insensitive unless \c CaseSensitive is set. \c
+ CaseSensitive can be OR-ed with any combination of the other
+ flags.
+
+ \sa ComparisonFlags
+*/
+
+/*!
+ \typedef Q3ListView::ComparisonFlags
+
+ This typedef is used in Q3ListView's API for values that are OR'd
+ combinations of \l StringComparisonMode values.
+
+ \sa StringComparisonMode
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LISTVIEW
diff --git a/src/qt3support/itemviews/q3listview.h b/src/qt3support/itemviews/q3listview.h
new file mode 100644
index 0000000..df958d5
--- /dev/null
+++ b/src/qt3support/itemviews/q3listview.h
@@ -0,0 +1,609 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3LISTVIEW_H
+#define Q3LISTVIEW_H
+
+#include <Qt3Support/q3scrollview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_LISTVIEW
+
+class QPixmap;
+class QFont;
+class Q3Header;
+class QIcon;
+
+class Q3ListView;
+struct Q3ListViewPrivate;
+struct Q3CheckListItemPrivate;
+class Q3ListViewItemIterator;
+struct Q3ListViewItemIteratorPrivate;
+class Q3DragObject;
+class QMimeSource;
+class QLineEdit;
+class Q3ListViewToolTip;
+
+class Q_COMPAT_EXPORT Q3ListViewItem
+{
+ friend class Q3ListViewItemIterator;
+ friend class Q3ListViewToolTip;
+
+public:
+ Q3ListViewItem(Q3ListView * parent);
+ Q3ListViewItem(Q3ListViewItem * parent);
+ Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after);
+ Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after);
+
+ Q3ListViewItem(Q3ListView * parent,
+ const QString&, const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString());
+ Q3ListViewItem(Q3ListViewItem * parent,
+ const QString&, const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString());
+
+ Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after,
+ const QString&, const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString());
+ Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after,
+ const QString&, const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString(),
+ const QString& = QString(), const QString& = QString());
+ virtual ~Q3ListViewItem();
+
+ virtual void insertItem(Q3ListViewItem *);
+ virtual void takeItem(Q3ListViewItem *);
+ virtual void removeItem(Q3ListViewItem *item) { takeItem(item); }
+
+ int height() const;
+ virtual void invalidateHeight();
+ int totalHeight() const;
+ virtual int width(const QFontMetrics&,
+ const Q3ListView*, int column) const;
+ void widthChanged(int column=-1) const;
+ int depth() const;
+
+ virtual void setText(int, const QString &);
+ virtual QString text(int) const;
+
+ virtual void setPixmap(int, const QPixmap &);
+ virtual const QPixmap * pixmap(int) const;
+
+ virtual QString key(int, bool) const;
+ virtual int compare(Q3ListViewItem *i, int col, bool) const;
+ virtual void sortChildItems(int, bool);
+
+ int childCount() const { return nChildren; }
+
+ bool isOpen() const { return open; }
+ virtual void setOpen(bool);
+ virtual void setup();
+
+ virtual void setSelected(bool);
+ bool isSelected() const { return selected; }
+
+ virtual void paintCell(QPainter *, const QColorGroup & cg,
+ int column, int width, int alignment);
+ virtual void paintBranches(QPainter * p, const QColorGroup & cg, int w, int y, int h);
+ virtual void paintFocus(QPainter *, const QColorGroup & cg, const QRect & r);
+
+ Q3ListViewItem * firstChild() const;
+ Q3ListViewItem * nextSibling() const { return siblingItem; }
+ Q3ListViewItem * parent() const;
+
+ Q3ListViewItem * itemAbove() const;
+ Q3ListViewItem * itemBelow() const;
+
+ int itemPos() const;
+
+ Q3ListView *listView() const;
+
+ virtual void setSelectable(bool enable);
+ bool isSelectable() const { return selectable && enabled; }
+
+ virtual void setExpandable(bool);
+ bool isExpandable() const { return expandable; }
+
+ void repaint() const;
+
+ virtual void sort();
+ void moveItem(Q3ListViewItem *after);
+
+ virtual void setDragEnabled(bool allow);
+ virtual void setDropEnabled(bool allow);
+ bool dragEnabled() const;
+ bool dropEnabled() const;
+ virtual bool acceptDrop(const QMimeSource *mime) const;
+
+ void setVisible(bool b);
+ bool isVisible() const;
+
+ virtual void setRenameEnabled(int col, bool b);
+ bool renameEnabled(int col) const;
+ virtual void startRename(int col);
+
+ virtual void setEnabled(bool b);
+ bool isEnabled() const;
+
+ virtual int rtti() const;
+ enum { RTTI = 0 };
+
+ virtual void setMultiLinesEnabled(bool b);
+ bool multiLinesEnabled() const;
+
+protected:
+ virtual void enforceSortOrder() const;
+ virtual void setHeight(int);
+ virtual void activate();
+
+ bool activatedPos(QPoint &);
+#ifndef QT_NO_DRAGANDDROP
+ virtual void dropped(QDropEvent *e);
+#endif
+ virtual void dragEntered();
+ virtual void dragLeft();
+ virtual void okRename(int col);
+ virtual void cancelRename(int col);
+
+ void ignoreDoubleClick();
+
+private:
+ void init();
+ void moveToJustAfter(Q3ListViewItem *);
+ void enforceSortOrderBackToRoot();
+ void removeRenameBox();
+
+ int ownHeight;
+ int maybeTotalHeight;
+ int nChildren;
+
+ uint lsc: 14;
+ uint lso: 1;
+ uint open : 1;
+ uint selected : 1;
+ uint selectable: 1;
+ uint configured: 1;
+ uint expandable: 1;
+ uint is_root: 1;
+ uint allow_drag : 1;
+ uint allow_drop : 1;
+ uint visible : 1;
+ uint enabled : 1;
+ uint mlenabled : 1;
+
+ Q3ListViewItem * parentItem;
+ Q3ListViewItem * siblingItem;
+ Q3ListViewItem * childItem;
+ QLineEdit *renameBox;
+ int renameCol;
+
+ void * columns;
+
+ friend class Q3ListView;
+};
+
+class Q3CheckListItem;
+
+class Q_COMPAT_EXPORT Q3ListView : public Q3ScrollView
+{
+ friend class Q3ListViewItemIterator;
+ friend class Q3ListViewItem;
+ friend class Q3CheckListItem;
+ friend class Q3ListViewToolTip;
+
+ Q_OBJECT
+ Q_ENUMS(SelectionMode ResizeMode RenameAction)
+ Q_PROPERTY(int columns READ columns)
+ Q_PROPERTY(bool multiSelection READ isMultiSelection WRITE setMultiSelection DESIGNABLE false)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode)
+ Q_PROPERTY(int childCount READ childCount)
+ Q_PROPERTY(bool allColumnsShowFocus READ allColumnsShowFocus WRITE setAllColumnsShowFocus)
+ Q_PROPERTY(bool showSortIndicator READ showSortIndicator WRITE setShowSortIndicator)
+ Q_PROPERTY(int itemMargin READ itemMargin WRITE setItemMargin)
+ Q_PROPERTY(bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated)
+ Q_PROPERTY(bool showToolTips READ showToolTips WRITE setShowToolTips)
+ Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode)
+ Q_PROPERTY(int treeStepSize READ treeStepSize WRITE setTreeStepSize)
+ Q_PROPERTY(RenameAction defaultRenameAction READ defaultRenameAction WRITE setDefaultRenameAction)
+
+public:
+ Q3ListView(QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0);
+ ~Q3ListView();
+
+ int treeStepSize() const;
+ virtual void setTreeStepSize(int);
+
+ virtual void insertItem(Q3ListViewItem *);
+ virtual void takeItem(Q3ListViewItem *);
+ virtual void removeItem(Q3ListViewItem *item) { takeItem(item); }
+
+ Q3Header * header() const;
+
+ virtual int addColumn(const QString &label, int size = -1);
+ virtual int addColumn(const QIcon& icon, const QString &label, int size = -1);
+ virtual void removeColumn(int index);
+ virtual void setColumnText(int column, const QString &label);
+ virtual void setColumnText(int column, const QIcon& icon, const QString &label);
+ QString columnText(int column) const;
+ virtual void setColumnWidth(int column, int width);
+ int columnWidth(int column) const;
+ enum WidthMode { Manual, Maximum };
+ virtual void setColumnWidthMode(int column, WidthMode);
+ WidthMode columnWidthMode(int column) const;
+ int columns() const;
+
+ virtual void setColumnAlignment(int, int);
+ int columnAlignment(int) const;
+
+ void show();
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+ Q3ListViewItem * itemAt(const QPoint & screenPos) const;
+ QRect itemRect(const Q3ListViewItem *) const;
+ int itemPos(const Q3ListViewItem *);
+
+ void ensureItemVisible(const Q3ListViewItem *);
+
+ void repaintItem(const Q3ListViewItem *) const;
+
+ virtual void setMultiSelection(bool enable);
+ bool isMultiSelection() const;
+
+ enum SelectionMode { Single, Multi, Extended, NoSelection };
+ void setSelectionMode(SelectionMode mode);
+ SelectionMode selectionMode() const;
+
+ virtual void clearSelection();
+ virtual void setSelected(Q3ListViewItem *, bool);
+ void setSelectionAnchor(Q3ListViewItem *);
+ bool isSelected(const Q3ListViewItem *) const;
+ Q3ListViewItem * selectedItem() const;
+ virtual void setOpen(Q3ListViewItem *, bool);
+ bool isOpen(const Q3ListViewItem *) const;
+
+ virtual void setCurrentItem(Q3ListViewItem *);
+ Q3ListViewItem * currentItem() const;
+
+ Q3ListViewItem * firstChild() const;
+ Q3ListViewItem * lastItem() const;
+
+ int childCount() const;
+
+ virtual void setAllColumnsShowFocus(bool);
+ bool allColumnsShowFocus() const;
+
+ virtual void setItemMargin(int);
+ int itemMargin() const;
+
+ virtual void setRootIsDecorated(bool);
+ bool rootIsDecorated() const;
+
+ virtual void setSorting(int column, bool ascending = true);
+ int sortColumn() const;
+ void setSortColumn(int column);
+ Qt::SortOrder sortOrder() const;
+ void setSortOrder(Qt::SortOrder order);
+ virtual void sort();
+
+ bool eventFilter(QObject * o, QEvent *);
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ virtual void setShowSortIndicator(bool show);
+ bool showSortIndicator() const;
+ virtual void setShowToolTips(bool b);
+ bool showToolTips() const;
+
+ enum ResizeMode { NoColumn, AllColumns, LastColumn };
+ virtual void setResizeMode(ResizeMode m);
+ ResizeMode resizeMode() const;
+
+ enum StringComparisonMode {
+ CaseSensitive = 0x00001, // 0 0001
+ BeginsWith = 0x00002, // 0 0010
+ EndsWith = 0x00004, // 0 0100
+ Contains = 0x00008, // 0 1000
+ ExactMatch = 0x00010 // 1 0000
+ };
+ typedef uint ComparisonFlags;
+ Q3ListViewItem * findItem(const QString& text, int column,
+ ComparisonFlags = ExactMatch | Qt::CaseSensitive ) const;
+
+ enum RenameAction { Accept, Reject };
+ virtual void setDefaultRenameAction(RenameAction a);
+ RenameAction defaultRenameAction() const;
+ bool isRenaming() const;
+
+ void hideColumn(int column);
+
+public Q_SLOTS:
+ virtual void clear();
+ virtual void invertSelection();
+ virtual void selectAll(bool select);
+ void triggerUpdate();
+ void setContentsPos(int x, int y);
+ void adjustColumn(int col);
+
+Q_SIGNALS:
+ void selectionChanged();
+ void selectionChanged(Q3ListViewItem *);
+ void currentChanged(Q3ListViewItem *);
+ void clicked(Q3ListViewItem *);
+ void clicked(Q3ListViewItem *, const QPoint &, int);
+ void pressed(Q3ListViewItem *);
+ void pressed(Q3ListViewItem *, const QPoint &, int);
+
+ void doubleClicked(Q3ListViewItem *);
+ void doubleClicked(Q3ListViewItem *, const QPoint&, int);
+ void returnPressed(Q3ListViewItem *);
+ void spacePressed(Q3ListViewItem *);
+ void rightButtonClicked(Q3ListViewItem *, const QPoint&, int);
+ void rightButtonPressed(Q3ListViewItem *, const QPoint&, int);
+ void mouseButtonPressed(int, Q3ListViewItem *, const QPoint& , int);
+ void mouseButtonClicked(int, Q3ListViewItem *, const QPoint&, int);
+
+ void contextMenuRequested(Q3ListViewItem *, const QPoint &, int);
+
+ void onItem(Q3ListViewItem *item);
+ void onViewport();
+
+ void expanded(Q3ListViewItem *item);
+ void collapsed(Q3ListViewItem *item);
+#ifndef QT_NO_DRAGANDDROP
+ void dropped(QDropEvent *e);
+#endif
+ void itemRenamed(Q3ListViewItem *item, int col, const QString &);
+ void itemRenamed(Q3ListViewItem *item, int col );
+
+protected:
+ void contentsMousePressEvent(QMouseEvent * e);
+ void contentsMouseReleaseEvent(QMouseEvent * e);
+ void contentsMouseMoveEvent(QMouseEvent * e);
+ void contentsMouseDoubleClickEvent(QMouseEvent * e);
+ void contentsContextMenuEvent(QContextMenuEvent * e);
+#ifndef QT_NO_DRAGANDDROP
+ void contentsDragEnterEvent(QDragEnterEvent *e);
+ void contentsDragMoveEvent(QDragMoveEvent *e);
+ void contentsDragLeaveEvent(QDragLeaveEvent *e);
+ void contentsDropEvent(QDropEvent *e);
+ virtual Q3DragObject *dragObject();
+ virtual void startDrag();
+#endif
+
+ void focusInEvent(QFocusEvent * e);
+ void focusOutEvent(QFocusEvent * e);
+
+ void keyPressEvent(QKeyEvent *e);
+
+ void resizeEvent(QResizeEvent *e);
+ void viewportResizeEvent(QResizeEvent *e);
+
+ void showEvent(QShowEvent *);
+
+ void drawContentsOffset(QPainter *, int ox, int oy,
+ int cx, int cy, int cw, int ch);
+
+ virtual void paintEmptyArea(QPainter *, const QRect &);
+ void changeEvent(QEvent *);
+
+protected Q_SLOTS:
+ void updateContents();
+ void doAutoScroll();
+
+private Q_SLOTS:
+ void changeSortColumn(int);
+ void handleIndexChange();
+ void updateDirtyItems();
+ void makeVisible();
+ void handleSizeChange(int, int, int);
+ void startRename();
+ void openFocusItem();
+
+private:
+ Q_DISABLE_COPY(Q3ListView)
+
+ void contentsMousePressEventEx(QMouseEvent * e);
+ void contentsMouseReleaseEventEx(QMouseEvent * e);
+ void init();
+ void updateGeometries();
+ void buildDrawableList() const;
+ void reconfigureItems();
+ void widthChanged(const Q3ListViewItem*, int c);
+ void handleItemChange(Q3ListViewItem *old, bool shift, bool control);
+ void selectRange(Q3ListViewItem *from, Q3ListViewItem *to, bool invert, bool includeFirst, bool clearSel = false);
+ bool selectRange(Q3ListViewItem *newItem, Q3ListViewItem *oldItem, Q3ListViewItem *anchorItem);
+ bool clearRange(Q3ListViewItem *from, Q3ListViewItem *to, bool includeFirst = true);
+ void doAutoScroll(const QPoint &cursorPos);
+
+ Q3ListViewPrivate *d;
+};
+
+
+class Q_COMPAT_EXPORT Q3CheckListItem : public Q3ListViewItem
+{
+public:
+ enum Type { RadioButton,
+ CheckBox,
+ Controller,
+ RadioButtonController=Controller,
+ CheckBoxController };
+
+ enum ToggleState { Off, NoChange, On };
+
+ Q3CheckListItem(Q3CheckListItem *parent, const QString &text,
+ Type = RadioButtonController);
+ Q3CheckListItem(Q3CheckListItem *parent, Q3ListViewItem *after,
+ const QString &text, Type = RadioButtonController);
+ Q3CheckListItem(Q3ListViewItem *parent, const QString &text,
+ Type = RadioButtonController);
+ Q3CheckListItem(Q3ListViewItem *parent, Q3ListViewItem *after,
+ const QString &text, Type = RadioButtonController);
+ Q3CheckListItem(Q3ListView *parent, const QString &text,
+ Type = RadioButtonController);
+ Q3CheckListItem(Q3ListView *parent, Q3ListViewItem *after,
+ const QString &text, Type = RadioButtonController);
+ Q3CheckListItem(Q3ListViewItem *parent, const QString &text,
+ const QPixmap &);
+ Q3CheckListItem(Q3ListView *parent, const QString &text,
+ const QPixmap &);
+ ~Q3CheckListItem();
+
+ void paintCell(QPainter *, const QColorGroup & cg,
+ int column, int width, int alignment);
+ virtual void paintFocus(QPainter *, const QColorGroup &cg,
+ const QRect & r);
+ int width(const QFontMetrics&, const Q3ListView*, int column) const;
+ void setup();
+
+ virtual void setOn(bool);
+ bool isOn() const { return on; }
+ Type type() const { return myType; }
+ QString text() const { return Q3ListViewItem::text(0); }
+ QString text(int n) const { return Q3ListViewItem::text(n); }
+
+ void setTristate(bool);
+ bool isTristate() const;
+ ToggleState state() const;
+ void setState(ToggleState s);
+
+ int rtti() const;
+ enum { RTTI = 1 };
+
+protected:
+ void activate();
+ void turnOffChild();
+ virtual void stateChange(bool);
+
+private:
+ void init();
+ ToggleState internalState() const;
+ void setStoredState(ToggleState newState, Q3CheckListItem *key);
+ ToggleState storedState(Q3CheckListItem *key) const;
+ void stateChange(ToggleState s);
+ void restoreState(Q3CheckListItem *key, int depth = 0);
+ void updateController(bool update = true , bool store = false);
+ void updateStoredState(Q3CheckListItem *key);
+ void setState(ToggleState s, bool update, bool store);
+ void setCurrentState(ToggleState s);
+
+ Type myType;
+ bool on;
+ Q3CheckListItemPrivate *d;
+};
+
+class Q_COMPAT_EXPORT Q3ListViewItemIterator
+{
+ friend struct Q3ListViewPrivate;
+ friend class Q3ListView;
+ friend class Q3ListViewItem;
+
+public:
+ enum IteratorFlag {
+ Visible = 0x00000001,
+ Invisible = 0x00000002,
+ Selected = 0x00000004,
+ Unselected = 0x00000008,
+ Selectable = 0x00000010,
+ NotSelectable = 0x00000020,
+ DragEnabled = 0x00000040,
+ DragDisabled = 0x00000080,
+ DropEnabled = 0x00000100,
+ DropDisabled = 0x00000200,
+ Expandable = 0x00000400,
+ NotExpandable = 0x00000800,
+ Checked = 0x00001000,
+ NotChecked = 0x00002000
+ };
+
+ Q3ListViewItemIterator();
+ Q3ListViewItemIterator(Q3ListViewItem *item);
+ Q3ListViewItemIterator(Q3ListViewItem *item, int iteratorFlags);
+
+ Q3ListViewItemIterator(const Q3ListViewItemIterator &it);
+ Q3ListViewItemIterator(Q3ListView *lv);
+ Q3ListViewItemIterator(Q3ListView *lv, int iteratorFlags);
+
+ Q3ListViewItemIterator &operator=(const Q3ListViewItemIterator &it);
+
+ ~Q3ListViewItemIterator();
+
+ Q3ListViewItemIterator &operator++();
+ const Q3ListViewItemIterator operator++(int);
+ Q3ListViewItemIterator &operator+=(int j);
+
+ Q3ListViewItemIterator &operator--();
+ const Q3ListViewItemIterator operator--(int);
+ Q3ListViewItemIterator &operator-=(int j);
+
+ Q3ListViewItem* operator*();
+ Q3ListViewItem *current() const;
+
+private:
+ Q3ListViewItem *curr;
+ Q3ListView *listView;
+ int flags;
+
+ void currentRemoved();
+ bool matchesFlags(const Q3ListViewItem*) const;
+ bool testPair(Q3ListViewItemIterator::IteratorFlag, Q3ListViewItemIterator::IteratorFlag, bool) const;
+ bool isChecked(const Q3ListViewItem*) const;
+};
+
+#endif // QT_NO_LISTVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3LISTVIEW_H
diff --git a/src/qt3support/itemviews/q3table.cpp b/src/qt3support/itemviews/q3table.cpp
new file mode 100644
index 0000000..8295e4e
--- /dev/null
+++ b/src/qt3support/itemviews/q3table.cpp
@@ -0,0 +1,7334 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#if defined(Q_CC_BOR)
+// needed for qsort() because of a std namespace problem on Borland
+#include "qplatformdefs.h"
+#endif
+
+#include "q3table.h"
+
+
+#include <qpainter.h>
+#include <qlineedit.h>
+#include <qcursor.h>
+#include <qapplication.h>
+#include <qtimer.h>
+#include <qicon.h>
+#include <q3combobox.h>
+#include <qstyleoption.h>
+#include <qcheckbox.h>
+#include <q3dragobject.h>
+#include <qevent.h>
+#include <q3listbox.h>
+#include <qstyle.h>
+#include <q3datatable.h>
+#include <qvalidator.h>
+#include <q3button.h>
+
+#include <stdlib.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+class Q3HeaderData;
+extern bool qt_get_null_label_bit(Q3HeaderData *data, int section);
+extern void qt_set_null_label_bit(Q3HeaderData *data, int section, bool b);
+
+static bool qt_update_cell_widget = true;
+static bool qt_table_clipper_enabled = true;
+#ifndef QT_INTERNAL_TABLE
+Q_COMPAT_EXPORT
+#endif
+void qt_set_table_clipper_enabled(bool enabled)
+{
+ qt_table_clipper_enabled = enabled;
+}
+
+class Q_COMPAT_EXPORT Q3TableHeader : public Q3Header
+{
+ friend class Q3Table;
+ Q_OBJECT
+
+public:
+ enum SectionState {
+ Normal,
+ Bold,
+ Selected
+ };
+
+ Q3TableHeader(int, Q3Table *t, QWidget* parent=0, const char* name=0);
+ ~Q3TableHeader() {};
+ void addLabel(const QString &s, int size);
+ void setLabel(int section, const QString & s, int size = -1);
+ void setLabel(int section, const QIconSet & iconset, const QString & s,
+ int size = -1);
+
+ void setLabels(const QStringList & labels);
+
+ void removeLabel(int section);
+
+ void setSectionState(int s, SectionState state);
+ void setSectionStateToAll(SectionState state);
+ SectionState sectionState(int s) const;
+
+ int sectionSize(int section) const;
+ int sectionPos(int section) const;
+ int sectionAt(int section) const;
+
+ void setSectionStretchable(int s, bool b);
+ bool isSectionStretchable(int s) const;
+
+ void updateCache();
+
+signals:
+ void sectionSizeChanged(int s);
+
+protected:
+ void paintEvent(QPaintEvent *e);
+ void paintSection(QPainter *p, int index, const QRect& fr);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void mouseDoubleClickEvent(QMouseEvent *e);
+ void resizeEvent(QResizeEvent *e);
+
+private slots:
+ void doAutoScroll();
+ void sectionWidthChanged(int col, int os, int ns);
+ void indexChanged(int sec, int oldIdx, int newIdx);
+ void updateStretches();
+ void updateWidgetStretches();
+
+private:
+ void updateSelections();
+ void saveStates();
+ void setCaching(bool b);
+ void swapSections(int oldIdx, int newIdx, bool swapTable = true);
+ bool doSelection(QMouseEvent *e);
+ void sectionLabelChanged(int section);
+ void resizeArrays(int n);
+
+private:
+ Q3MemArray<int> states, oldStates;
+ Q3MemArray<bool> stretchable;
+ Q3MemArray<int> sectionSizes, sectionPoses;
+ bool mousePressed;
+ int pressPos, startPos, endPos;
+ Q3Table *table;
+ QTimer *autoScrollTimer;
+ QWidget *line1, *line2;
+ bool caching;
+ int resizedSection;
+ bool isResizing;
+ int numStretches;
+ QTimer *stretchTimer, *widgetStretchTimer;
+ Q3TableHeaderPrivate *d;
+
+ Q_DISABLE_COPY(Q3TableHeader)
+};
+
+#ifdef _WS_QWS_
+# define NO_LINE_WIDGET
+#endif
+
+
+
+struct Q3TablePrivate
+{
+ Q3TablePrivate() : hasRowSpan(false), hasColSpan(false),
+ inMenuMode(false), redirectMouseEvent(false)
+ {
+ hiddenRows.setAutoDelete(true);
+ hiddenCols.setAutoDelete(true);
+ }
+ uint hasRowSpan : 1;
+ uint hasColSpan : 1;
+ uint inMenuMode : 1;
+ uint redirectMouseEvent : 1;
+ Q3IntDict<int> hiddenRows, hiddenCols;
+ QTimer *geomTimer;
+ int lastVisRow;
+ int lastVisCol;
+};
+
+struct Q3TableHeaderPrivate
+{
+#ifdef NO_LINE_WIDGET
+ int oldLinePos;
+#endif
+};
+
+static bool isRowSelection(Q3Table::SelectionMode selMode)
+{
+ return selMode == Q3Table::SingleRow || selMode == Q3Table::MultiRow;
+}
+
+/*!
+ \class Q3TableSelection
+ \brief The Q3TableSelection class provides access to a selected area in a
+ Q3Table.
+
+ \compat
+
+ The selection is a rectangular set of cells in a Q3Table. One of
+ the rectangle's cells is called the anchor cell; this is the cell
+ that was selected first. The init() function sets the anchor and
+ the selection rectangle to exactly this cell; the expandTo()
+ function expands the selection rectangle to include additional
+ cells.
+
+ There are various access functions to find out about the area:
+ anchorRow() and anchorCol() return the anchor's position;
+ leftCol(), rightCol(), topRow() and bottomRow() return the
+ rectangle's four edges. All four are part of the selection.
+
+ A newly created Q3TableSelection is inactive -- isActive() returns
+ false. You must use init() and expandTo() to activate it.
+
+ \sa Q3Table Q3Table::addSelection() Q3Table::selection()
+ Q3Table::selectCells() Q3Table::selectRow() Q3Table::selectColumn()
+*/
+
+/*!
+ Creates an inactive selection. Use init() and expandTo() to
+ activate it.
+*/
+
+Q3TableSelection::Q3TableSelection()
+ : active(false), inited(false), tRow(-1), lCol(-1),
+ bRow(-1), rCol(-1), aRow(-1), aCol(-1)
+{
+}
+
+/*!
+ Creates an active selection, starting at \a start_row and \a
+ start_col, ending at \a end_row and \a end_col.
+*/
+
+Q3TableSelection::Q3TableSelection(int start_row, int start_col, int end_row, int end_col)
+ : active(false), inited(false), tRow(-1), lCol(-1),
+ bRow(-1), rCol(-1), aRow(-1), aCol(-1)
+{
+ init(start_row, start_col);
+ expandTo(end_row, end_col);
+}
+
+/*!
+ Sets the selection anchor to cell \a row, \a col and the selection
+ to only contain this cell. The selection is not active until
+ expandTo() is called.
+
+ To extend the selection to include additional cells, call
+ expandTo().
+
+ \sa isActive()
+*/
+
+void Q3TableSelection::init(int row, int col)
+{
+ aCol = lCol = rCol = col;
+ aRow = tRow = bRow = row;
+ active = false;
+ inited = true;
+}
+
+/*!
+ Expands the selection to include cell \a row, \a col. The new
+ selection rectangle is the bounding rectangle of \a row, \a col
+ and the previous selection rectangle. After calling this function
+ the selection is active.
+
+ If you haven't called init(), this function does nothing.
+
+ \sa init() isActive()
+*/
+
+void Q3TableSelection::expandTo(int row, int col)
+{
+ if (!inited)
+ return;
+ active = true;
+
+ if (row < aRow) {
+ tRow = row;
+ bRow = aRow;
+ } else {
+ tRow = aRow;
+ bRow = row;
+ }
+
+ if (col < aCol) {
+ lCol = col;
+ rCol = aCol;
+ } else {
+ lCol = aCol;
+ rCol = col;
+ }
+}
+
+/*!
+ Returns true if \a s includes the same cells as the selection;
+ otherwise returns false.
+*/
+
+bool Q3TableSelection::operator==(const Q3TableSelection &s) const
+{
+ return (s.active == active &&
+ s.tRow == tRow && s.bRow == bRow &&
+ s.lCol == lCol && s.rCol == rCol);
+}
+
+/*!
+ \fn bool Q3TableSelection::operator!=(const Q3TableSelection &s) const
+
+ Returns true if \a s does not include the same cells as the
+ selection; otherwise returns false.
+*/
+
+
+/*!
+ \fn int Q3TableSelection::topRow() const
+
+ Returns the top row of the selection.
+
+ \sa bottomRow() leftCol() rightCol()
+*/
+
+/*!
+ \fn int Q3TableSelection::bottomRow() const
+
+ Returns the bottom row of the selection.
+
+ \sa topRow() leftCol() rightCol()
+*/
+
+/*!
+ \fn int Q3TableSelection::leftCol() const
+
+ Returns the left column of the selection.
+
+ \sa topRow() bottomRow() rightCol()
+*/
+
+/*!
+ \fn int Q3TableSelection::rightCol() const
+
+ Returns the right column of the selection.
+
+ \sa topRow() bottomRow() leftCol()
+*/
+
+/*!
+ \fn int Q3TableSelection::anchorRow() const
+
+ Returns the anchor row of the selection.
+
+ \sa anchorCol() expandTo()
+*/
+
+/*!
+ \fn int Q3TableSelection::anchorCol() const
+
+ Returns the anchor column of the selection.
+
+ \sa anchorRow() expandTo()
+*/
+
+/*!
+ \fn int Q3TableSelection::numRows() const
+
+ Returns the number of rows in the selection.
+
+ \sa numCols()
+*/
+int Q3TableSelection::numRows() const
+{
+ return (tRow < 0) ? 0 : bRow - tRow + 1;
+}
+
+/*!
+ Returns the number of columns in the selection.
+
+ \sa numRows()
+*/
+int Q3TableSelection::numCols() const
+{
+ return (lCol < 0) ? 0 : rCol - lCol + 1;
+}
+
+/*!
+ \fn bool Q3TableSelection::isActive() const
+
+ Returns whether the selection is active or not. A selection is
+ active after init() \e and expandTo() have been called.
+*/
+
+/*!
+ \fn bool Q3TableSelection::isEmpty() const
+
+ Returns whether the selection is empty or not.
+
+ \sa numRows(), numCols()
+*/
+
+/*!
+ \class Q3TableItem
+ \brief The Q3TableItem class provides the cell content for Q3Table cells.
+
+ \compat
+
+ For many applications Q3TableItems are ideal for presenting and
+ editing the contents of Q3Table cells. In situations where you need
+ to create very large tables you may prefer an alternative approach
+ to using Q3TableItems: see the notes on large tables.
+
+ A Q3TableItem contains a cell's data, by default, a string and a
+ pixmap. The table item also holds the cell's display size and how
+ the data should be aligned. The table item specifies the cell's
+ \l EditType and the editor used for in-place editing (by default a
+ QLineEdit). If you want checkboxes use \l{Q3CheckTableItem}, and if
+ you want comboboxes use \l{Q3ComboTableItem}. The \l EditType (set
+ in the constructor) determines whether the cell's contents may be
+ edited.
+
+ If a pixmap is specified it is displayed to the left of any text.
+ You can change the text or pixmap with setText() and setPixmap()
+ respectively. For text you can use setWordWrap().
+
+ When sorting table items the key() function is used; by default
+ this returns the table item's text(). Reimplement key() to
+ customize how your table items will sort.
+
+ Table items are inserted into a table using Q3Table::setItem(). If
+ you insert an item into a cell that already contains a table item
+ the original item will be deleted.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 0
+
+ You can move a table item from one cell to another, in the same or
+ a different table, using Q3Table::takeItem() and Q3Table::setItem()
+ but see also Q3Table::swapCells().
+
+ Table items can be deleted with delete in the standard way; the
+ table and cell will be updated accordingly.
+
+ Note, that if you have a table item that is not currently in a table
+ then anything you do to that item other than insert it into a table
+ will result in undefined behaviour.
+
+ Reimplement createEditor() and setContentFromEditor() if you want
+ to use your own widget instead of a QLineEdit for editing cell
+ contents. Reimplement paint() if you want to display custom
+ content.
+
+ It is important to ensure that your custom widget can accept the
+ keyboard focus, so that the user can use the tab key to navigate the
+ table as normal. Therefore, if the widget returned by createEditor()
+ does not itself accept the keyboard focus, it is necessary to
+ nominate a child widget to do so on its behalf. For example, a
+ QHBox with two child QLineEdit widgets may use one of them to
+ accept the keyboard focus:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 1
+
+ By default, table items may be replaced by new Q3TableItems
+ during the lifetime of a Q3Table. Therefore, if you create your
+ own subclass of Q3TableItem, and you want to ensure that
+ this does not happen, you must call setReplaceable(false)
+ in the constructor of your subclass.
+
+ \img qtableitems.png Table Items
+
+ \sa Q3CheckTableItem Q3ComboTableItem
+
+*/
+
+/*!
+ \fn Q3Table *Q3TableItem::table() const
+
+ Returns the Q3Table the table item belongs to.
+
+ \sa Q3Table::setItem() Q3TableItem()
+*/
+
+/*!
+ \enum Q3TableItem::EditType
+
+ \target wheneditable
+ This enum is used to define whether a cell is editable or
+ read-only (in conjunction with other settings), and how the cell
+ should be displayed.
+
+ \value Always
+ The cell always \e looks editable.
+
+ Using this EditType ensures that the editor created with
+ createEditor() (by default a QLineEdit) is always visible. This
+ has implications for the alignment of the content: the default
+ editor aligns everything (even numbers) to the left whilst
+ numerical values in the cell are by default aligned to the right.
+
+ If a cell with the edit type \c Always looks misaligned you could
+ reimplement createEditor() for these items.
+
+ \value WhenCurrent
+ The cell \e looks editable only when it has keyboard focus (see
+ Q3Table::setCurrentCell()).
+
+ \value OnTyping
+ The cell \e looks editable only when the user types in it or
+ double-clicks it. It resembles the \c WhenCurrent functionality
+ but is, perhaps, nicer.
+
+ The \c OnTyping edit type is the default when Q3TableItem objects
+ are created by the convenience functions Q3Table::setText() and
+ Q3Table::setPixmap().
+
+ \value Never The cell is not editable.
+
+ The cell is actually editable only if Q3Table::isRowReadOnly() is
+ false for its row, Q3Table::isColumnReadOnly() is false for its
+ column, and Q3Table::isReadOnly() is false.
+
+ Q3ComboTableItems have an isEditable() property. This property is
+ used to indicate whether the user may enter their own text or are
+ restricted to choosing one of the choices in the list.
+ Q3ComboTableItems may be interacted with only if they are editable
+ in accordance with their EditType as described above.
+
+*/
+
+/*!
+ Creates a table item that is a child of table \a table with no
+ text. The item has the \l EditType \a et.
+
+ The table item will use a QLineEdit for its editor, will not
+ word-wrap and will occupy a single cell. Insert the table item
+ into a table with Q3Table::setItem().
+
+ The table takes ownership of the table item, so a table item
+ should not be inserted into more than one table at a time.
+*/
+
+Q3TableItem::Q3TableItem(Q3Table *table, EditType et)
+ : txt(), pix(), t(table), edType(et), wordwrap(false),
+ tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1)
+{
+ enabled = true;
+}
+
+/*!
+ Creates a table item that is a child of table \a table with text
+ \a text. The item has the \l EditType \a et.
+
+ The table item will use a QLineEdit for its editor, will not
+ word-wrap and will occupy a single cell. Insert the table item
+ into a table with Q3Table::setItem().
+
+ The table takes ownership of the table item, so a table item
+ should not be inserted into more than one table at a time.
+*/
+
+Q3TableItem::Q3TableItem(Q3Table *table, EditType et, const QString &text)
+ : txt(text), pix(), t(table), edType(et), wordwrap(false),
+ tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1)
+{
+ enabled = true;
+}
+
+/*!
+ Creates a table item that is a child of table \a table with text
+ \a text and pixmap \a p. The item has the \l EditType \a et.
+
+ The table item will display the pixmap to the left of the text. It
+ will use a QLineEdit for editing the text, will not word-wrap and
+ will occupy a single cell. Insert the table item into a table with
+ Q3Table::setItem().
+
+ The table takes ownership of the table item, so a table item
+ should not be inserted in more than one table at a time.
+*/
+
+Q3TableItem::Q3TableItem(Q3Table *table, EditType et,
+ const QString &text, const QPixmap &p)
+ : txt(text), pix(p), t(table), edType(et), wordwrap(false),
+ tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1)
+{
+ enabled = true;
+}
+
+/*!
+ The destructor deletes this item and frees all allocated
+ resources.
+
+ If the table item is in a table (i.e. was inserted with
+ setItem()), it will be removed from the table and the cell it
+ occupied.
+*/
+
+Q3TableItem::~Q3TableItem()
+{
+ if (table())
+ table()->takeItem(this);
+}
+
+int Q3TableItem::RTTI = 0;
+
+/*!
+ Returns the Run Time Type Identification value for this table item
+ which for Q3TableItems is 0.
+
+ When you create subclasses based on Q3TableItem make sure that each
+ subclass returns a unique rtti() value. It is advisable to use
+ values greater than 1000, preferably large random numbers, to
+ allow for extensions to this class.
+
+ \sa Q3CheckTableItem::rtti() Q3ComboTableItem::rtti()
+*/
+
+int Q3TableItem::rtti() const
+{
+ return RTTI;
+}
+
+/*!
+ Returns the table item's pixmap or a null pixmap if no pixmap has
+ been set.
+
+ \sa setPixmap() text()
+*/
+
+QPixmap Q3TableItem::pixmap() const
+{
+ return pix;
+}
+
+
+/*!
+ Returns the text of the table item or an empty string if there is
+ no text.
+
+ To ensure that the current value of the editor is returned,
+ setContentFromEditor() is called:
+ \list 1
+ \i if the editMode() is \c Always, or
+ \i if editMode() is \e not \c Always but the editor of the cell is
+ active and the editor is not a QLineEdit.
+ \endlist
+
+ This means that text() returns the original text value of the item
+ if the editor is a line edit, until the user commits an edit (e.g.
+ by pressing Enter or Tab) in which case the new text is returned.
+ For other editors (e.g. a combobox) setContentFromEditor() is
+ always called so the currently display value is the one returned.
+
+ \sa setText() pixmap()
+*/
+
+QString Q3TableItem::text() const
+{
+ QWidget *w = table()->cellWidget(rw, cl);
+ if (w && (edType == Always ||
+ rtti() == Q3ComboTableItem::RTTI ||
+ rtti() == Q3CheckTableItem::RTTI))
+ ((Q3TableItem*)this)->setContentFromEditor(w);
+ return txt;
+}
+
+/*!
+ Sets pixmap \a p to be this item's pixmap.
+
+ Note that setPixmap() does not update the cell the table item
+ belongs to. Use Q3Table::updateCell() to repaint the cell's
+ contents.
+
+ For \l{Q3ComboTableItem}s and \l{Q3CheckTableItem}s this function
+ has no visible effect.
+
+ \sa Q3Table::setPixmap() pixmap() setText()
+*/
+
+void Q3TableItem::setPixmap(const QPixmap &p)
+{
+ pix = p;
+}
+
+/*!
+ Changes the table item's text to \a str.
+
+ Note that setText() does not update the cell the table item
+ belongs to. Use Q3Table::updateCell() to repaint the cell's
+ contents.
+
+ \sa Q3Table::setText() text() setPixmap() Q3Table::updateCell()
+*/
+
+void Q3TableItem::setText(const QString &str)
+{
+ txt = str;
+}
+
+/*!
+ This virtual function is used to paint the contents of an item
+ using the painter \a p in the rectangular area \a cr using the
+ color group \a cg.
+
+ If \a selected is true the cell is displayed in a way that
+ indicates that it is highlighted.
+
+ You don't usually need to use this function but if you want to
+ draw custom content in a cell you will need to reimplement it.
+
+ The painter passed to this function is translated so that 0, 0
+ is the top-left corner of the item that is being painted.
+
+ Note that the painter is not clipped by default in order to get
+ maximum efficiency. If you want clipping, use
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 2
+
+*/
+
+void Q3TableItem::paint(QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected)
+{
+ p->fillRect(0, 0, cr.width(), cr.height(),
+ selected ? cg.brush(QColorGroup::Highlight)
+ : cg.brush(QColorGroup::Base));
+
+ int w = cr.width();
+ int h = cr.height();
+
+ int x = 0;
+ if (!pix.isNull()) {
+ p->drawPixmap(0, (cr.height() - pix.height()) / 2, pix);
+ x = pix.width() + 2;
+ }
+
+ if (selected)
+ p->setPen(cg.highlightedText());
+ else
+ p->setPen(cg.text());
+ p->drawText(x + 2, 0, w - x - 4, h,
+ wordwrap ? (alignment() | WordBreak) : alignment(), text());
+}
+
+/*!
+This virtual function creates an editor which the user can
+interact with to edit the cell's contents. The default
+implementation creates a QLineEdit.
+
+If the function returns 0, the cell is read-only.
+
+The returned widget should preferably be invisible, ideally with
+Q3Table::viewport() as parent.
+
+If you reimplement this function you'll almost certainly need to
+reimplement setContentFromEditor(), and may need to reimplement
+sizeHint().
+
+\sa Q3Table::createEditor() setContentFromEditor() Q3Table::viewport() setReplaceable()
+*/
+
+QWidget *Q3TableItem::createEditor() const
+{
+ QLineEdit *e = new QLineEdit(table()->viewport(), "qt_tableeditor");
+ e->setFrame(false);
+ e->setText(text());
+ return e;
+}
+
+/*!
+Whenever the content of a cell has been edited by the editor \a w,
+Q3Table calls this virtual function to copy the new values into the
+Q3TableItem.
+
+If you reimplement createEditor() and return something that is not
+a QLineEdit you will need to reimplement this function.
+
+\sa Q3Table::setCellContentFromEditor()
+*/
+
+void Q3TableItem::setContentFromEditor(QWidget *w)
+{
+ QLineEdit *le = qobject_cast<QLineEdit*>(w);
+ if (le) {
+ QString input = le->text();
+ if (le->validator())
+ le->validator()->fixup(input);
+ setText(input);
+ }
+}
+
+/*!
+ The alignment function returns how the text contents of the cell
+ are aligned when drawn. The default implementation aligns numbers
+ to the right and any other text to the left.
+
+ \sa Qt::Alignment
+*/
+
+// ed: For consistency reasons a setAlignment() should be provided
+// as well.
+
+int Q3TableItem::alignment() const
+{
+ bool num;
+ bool ok1 = false, ok2 = false;
+ (void)text().toInt(&ok1);
+ if (!ok1)
+ (void)text().toDouble(&ok2); // ### should be .-aligned
+ num = ok1 || ok2;
+
+ return (num ? AlignRight : AlignLeft) | AlignVCenter;
+}
+
+/*!
+ If \a b is true, the cell's text will be wrapped over multiple
+ lines, when necessary, to fit the width of the cell; otherwise the
+ text will be written as a single line.
+
+ \sa wordWrap() Q3Table::adjustColumn() Q3Table::setColumnStretchable()
+*/
+
+void Q3TableItem::setWordWrap(bool b)
+{
+ wordwrap = b;
+}
+
+/*!
+ Returns true if word wrap is enabled for the cell; otherwise
+ returns false.
+
+ \sa setWordWrap()
+*/
+
+bool Q3TableItem::wordWrap() const
+{
+ return wordwrap;
+}
+
+/*! \internal */
+
+void Q3TableItem::updateEditor(int oldRow, int oldCol)
+{
+ if (edType != Always)
+ return;
+ if (oldRow != -1 && oldCol != -1)
+ table()->clearCellWidget(oldRow, oldCol);
+ if (rw != -1 && cl != -1)
+ table()->setCellWidget(rw, cl, createEditor());
+}
+
+/*!
+ Returns the table item's edit type.
+
+ This is set when the table item is constructed.
+
+ \sa EditType Q3TableItem()
+*/
+
+Q3TableItem::EditType Q3TableItem::editType() const
+{
+ return edType;
+}
+
+/*!
+ If \a b is true it is acceptable to replace the contents of the
+ cell with the contents of another Q3TableItem. If \a b is false the
+ contents of the cell may not be replaced by the contents of
+ another table item. Table items that span more than one cell may
+ not have their contents replaced by another table item.
+
+ (This differs from \l EditType because EditType is concerned with
+ whether the \e user is able to change the contents of a cell.)
+
+ \sa isReplaceable()
+*/
+
+void Q3TableItem::setReplaceable(bool b)
+{
+ tcha = b;
+}
+
+/*!
+ This function returns whether the contents of the cell may be
+ replaced with the contents of another table item. Regardless of
+ this setting, table items that span more than one cell may not
+ have their contents replaced by another table item.
+
+ (This differs from \l EditType because EditType is concerned with
+ whether the \e user is able to change the contents of a cell.)
+
+ \sa setReplaceable() EditType
+*/
+
+bool Q3TableItem::isReplaceable() const
+{
+ if (rowspan > 1 || colspan > 1)
+ return false;
+ return tcha;
+}
+
+/*!
+ This virtual function returns the key that should be used for
+ sorting. The default implementation returns the text() of the
+ relevant item.
+
+ \sa Q3Table::setSorting()
+*/
+
+QString Q3TableItem::key() const
+{
+ return text();
+}
+
+/*!
+ This virtual function returns the size a cell needs to show its
+ entire content.
+
+ If you subclass Q3TableItem you will often need to reimplement this
+ function.
+*/
+
+QSize Q3TableItem::sizeHint() const
+{
+ QSize strutSize = QApplication::globalStrut();
+ if (edType == Always && table()->cellWidget(rw, cl))
+ return table()->cellWidget(rw, cl)->sizeHint().expandedTo(strutSize);
+
+ QSize s;
+ int x = 0;
+ if (!pix.isNull()) {
+ s = pix.size();
+ s.setWidth(s.width() + 2);
+ x = pix.width() + 2;
+ }
+
+ QString t = text();
+ if (!wordwrap && t.find(QLatin1Char('\n')) == -1)
+ return QSize(s.width() + table()->fontMetrics().width(text()) + 10,
+ QMAX(s.height(), table()->fontMetrics().height())).expandedTo(strutSize);
+
+ QRect r = table()->fontMetrics().boundingRect(x + 2, 0, table()->columnWidth(col()) - x - 4, 0,
+ wordwrap ? (alignment() | WordBreak) : alignment(),
+ text());
+ r.setWidth(QMAX(r.width() + 10, table()->columnWidth(col())));
+ return QSize(r.width(), QMAX(s.height(), r.height())).expandedTo(strutSize);
+}
+
+/*!
+ Changes the extent of the Q3TableItem so that it spans multiple
+ cells covering \a rs rows and \a cs columns. The top left cell is
+ the original cell.
+
+ \warning This function only works if the item has already been
+ inserted into the table using e.g. Q3Table::setItem(). This
+ function also checks to make sure if \a rs and \a cs are within
+ the bounds of the table and returns without changing the span if
+ they are not. In addition swapping, inserting or removing rows and
+ columns that cross Q3TableItems spanning more than one cell is not
+ supported.
+
+ \sa rowSpan() colSpan()
+*/
+
+void Q3TableItem::setSpan(int rs, int cs)
+{
+ if (rs == rowspan && cs == colspan)
+ return;
+
+ if (!table()->d->hasRowSpan)
+ table()->d->hasRowSpan = rs > 1;
+ if (!table()->d->hasColSpan)
+ table()->d->hasColSpan = cs > 1;
+ // return if we are thinking too big...
+ if (rw + rs > table()->numRows())
+ return;
+
+ if (cl + cs > table()->numCols())
+ return;
+
+ if (rw == -1 || cl == -1)
+ return;
+
+ int rrow = rw;
+ int rcol = cl;
+ if (rowspan > 1 || colspan > 1) {
+ Q3Table* t = table();
+ t->takeItem(this);
+ t->setItem(rrow, rcol, this);
+ }
+
+ rowspan = rs;
+ colspan = cs;
+
+ for (int r = 0; r < rowspan; ++r) {
+ for (int c = 0; c < colspan; ++c) {
+ if (r == 0 && c == 0)
+ continue;
+ qt_update_cell_widget = false;
+ table()->setItem(r + rw, c + cl, this);
+ qt_update_cell_widget = true;
+ rw = rrow;
+ cl = rcol;
+ }
+ }
+
+ table()->updateCell(rw, cl);
+ QWidget *w = table()->cellWidget(rw, cl);
+ if (w)
+ w->resize(table()->cellGeometry(rw, cl).size());
+}
+
+/*!
+ Returns the row span of the table item, usually 1.
+
+ \sa setSpan() colSpan()
+*/
+
+int Q3TableItem::rowSpan() const
+{
+ return rowspan;
+}
+
+/*!
+ Returns the column span of the table item, usually 1.
+
+ \sa setSpan() rowSpan()
+*/
+
+int Q3TableItem::colSpan() const
+{
+ return colspan;
+}
+
+/*!
+ Sets row \a r as the table item's row. Usually you do not need to
+ call this function.
+
+ If the cell spans multiple rows, this function sets the top row
+ and retains the height of the multi-cell table item.
+
+ \sa row() setCol() rowSpan()
+*/
+
+void Q3TableItem::setRow(int r)
+{
+ rw = r;
+}
+
+/*!
+ Sets column \a c as the table item's column. Usually you will not
+ need to call this function.
+
+ If the cell spans multiple columns, this function sets the
+ left-most column and retains the width of the multi-cell table
+ item.
+
+ \sa col() setRow() colSpan()
+*/
+
+void Q3TableItem::setCol(int c)
+{
+ cl = c;
+}
+
+/*!
+ Returns the row where the table item is located. If the cell spans
+ multiple rows, this function returns the top-most row.
+
+ \sa col() setRow()
+*/
+
+int Q3TableItem::row() const
+{
+ return rw;
+}
+
+/*!
+ Returns the column where the table item is located. If the cell
+ spans multiple columns, this function returns the left-most
+ column.
+
+ \sa row() setCol()
+*/
+
+int Q3TableItem::col() const
+{
+ return cl;
+}
+
+/*!
+ If \a b is true, the table item is enabled; if \a b is false the
+ table item is disabled.
+
+ A disabled item doesn't respond to user interaction.
+
+ \sa isEnabled()
+*/
+
+void Q3TableItem::setEnabled(bool b)
+{
+ if (b == (bool)enabled)
+ return;
+ enabled = b;
+ table()->updateCell(row(), col());
+}
+
+/*!
+ Returns true if the table item is enabled; otherwise returns false.
+
+ \sa setEnabled()
+*/
+
+bool Q3TableItem::isEnabled() const
+{
+ return (bool)enabled;
+}
+
+/*!
+ \class Q3ComboTableItem
+ \brief The Q3ComboTableItem class provides a means of using
+ comboboxes in Q3Tables.
+
+ \compat
+
+ A Q3ComboTableItem is a table item which looks and behaves like a
+ combobox. The advantage of using Q3ComboTableItems rather than real
+ comboboxes is that a Q3ComboTableItem uses far less resources than
+ real comboboxes in \l{Q3Table}s. When the cell has the focus it
+ displays a real combobox which the user can interact with. When
+ the cell does not have the focus the cell \e looks like a
+ combobox. Only text items (i.e. no pixmaps) may be used in
+ Q3ComboTableItems.
+
+ Q3ComboTableItem items have the edit type \c WhenCurrent (see
+ \l{EditType}). The Q3ComboTableItem's list of items is provided by
+ a QStringList passed to the constructor.
+
+ The list of items may be changed using setStringList(). The
+ current item can be set with setCurrentItem() and retrieved with
+ currentItem(). The text of the current item can be obtained with
+ currentText(), and the text of a particular item can be retrieved
+ with text().
+
+ If isEditable() is true the Q3ComboTableItem will permit the user
+ to either choose an existing list item, or create a new list item
+ by entering their own text; otherwise the user may only choose one
+ of the existing list items.
+
+ To populate a table cell with a Q3ComboTableItem use
+ Q3Table::setItem().
+
+ Q3ComboTableItems may be deleted with Q3Table::clearCell().
+
+ Q3ComboTableItems can be distinguished from \l{Q3TableItem}s and
+ \l{Q3CheckTableItem}s using their Run Time Type Identification
+ number (see rtti()).
+
+ \img qtableitems.png Table Items
+
+ \sa Q3CheckTableItem Q3TableItem Q3ComboBox
+*/
+
+Q3ComboBox *Q3ComboTableItem::fakeCombo = 0;
+QWidget *Q3ComboTableItem::fakeComboWidget = 0;
+int Q3ComboTableItem::fakeRef = 0;
+
+/*!
+ Creates a combo table item for the table \a table. The combobox's
+ list of items is passed in the \a list argument. If \a editable is
+ true the user may type in new list items; if \a editable is false
+ the user may only select from the list of items provided.
+
+ By default Q3ComboTableItems cannot be replaced by other table
+ items since isReplaceable() returns false by default.
+
+ \sa Q3Table::clearCell() EditType
+*/
+
+Q3ComboTableItem::Q3ComboTableItem(Q3Table *table, const QStringList &list, bool editable)
+ : Q3TableItem(table, WhenCurrent, QLatin1String("")), entries(list), current(0), edit(editable)
+{
+ setReplaceable(false);
+ if (!Q3ComboTableItem::fakeCombo) {
+ Q3ComboTableItem::fakeComboWidget = new QWidget(0, 0);
+ Q3ComboTableItem::fakeCombo = new Q3ComboBox(false, Q3ComboTableItem::fakeComboWidget, 0);
+ Q3ComboTableItem::fakeCombo->hide();
+ }
+ ++Q3ComboTableItem::fakeRef;
+ if (entries.count())
+ setText(entries.at(current));
+}
+
+/*!
+ Q3ComboTableItem destructor.
+*/
+Q3ComboTableItem::~Q3ComboTableItem()
+{
+ if (--Q3ComboTableItem::fakeRef <= 0) {
+ delete Q3ComboTableItem::fakeComboWidget;
+ Q3ComboTableItem::fakeComboWidget = 0;
+ Q3ComboTableItem::fakeCombo = 0;
+ }
+}
+
+/*!
+ Sets the list items of this Q3ComboTableItem to the strings in the
+ string list \a l.
+*/
+
+void Q3ComboTableItem::setStringList(const QStringList &l)
+{
+ entries = l;
+ current = 0;
+ if (entries.count())
+ setText(entries.at(current));
+ if (table()->cellWidget(row(), col())) {
+ cb->clear();
+ cb->insertStringList(entries);
+ }
+ table()->updateCell(row(), col());
+}
+
+/*! \reimp */
+
+QWidget *Q3ComboTableItem::createEditor() const
+{
+ // create an editor - a combobox in our case
+ ((Q3ComboTableItem*)this)->cb = new Q3ComboBox(edit, table()->viewport(), "qt_editor_cb");
+ cb->insertStringList(entries);
+ cb->setCurrentItem(current);
+ QObject::connect(cb, SIGNAL(activated(int)), table(), SLOT(doValueChanged()));
+ return cb;
+}
+
+/*! \reimp */
+
+void Q3ComboTableItem::setContentFromEditor(QWidget *w)
+{
+ Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
+ if (cb) {
+ entries.clear();
+ for (int i = 0; i < cb->count(); ++i)
+ entries << cb->text(i);
+ current = cb->currentItem();
+ setText(cb->currentText());
+ }
+}
+
+/*! \reimp */
+
+void Q3ComboTableItem::paint(QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected)
+{
+ fakeCombo->resize(cr.width(), cr.height());
+
+ QPalette pal2(cg);
+ if (selected) {
+ pal2.setBrush(QPalette::Base, cg.QPalette::brush(QPalette::Highlight));
+ pal2.setColor(QPalette::Text, cg.highlightedText());
+ }
+
+ QStyle::State flags = QStyle::State_None;
+ if(isEnabled() && table()->isEnabled())
+ flags |= QStyle::State_Enabled;
+ // Since we still have the "fakeCombo" may as well use it in this case.
+ QStyleOptionComboBox opt;
+ opt.initFrom(table());
+ opt.rect = fakeCombo->rect();
+ opt.palette = pal2;
+ opt.state &= ~QStyle::State_HasFocus;
+ opt.state &= ~QStyle::State_MouseOver;
+ opt.state |= flags;
+ opt.subControls = QStyle::SC_All;
+ opt.activeSubControls = QStyle::SC_None;
+ opt.editable = fakeCombo->editable();
+ table()->style()->drawComplexControl(QStyle::CC_ComboBox, &opt, p, fakeCombo);
+
+ p->save();
+ QRect textR = table()->style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxEditField, fakeCombo);
+ int align = alignment(); // alignment() changes entries
+ p->drawText(textR, wordWrap() ? (align | Qt::WordBreak) : align, entries.value(current));
+ p->restore();
+}
+
+/*!
+ Sets the list item \a i to be the combo table item's current list
+ item.
+
+ \sa currentItem()
+*/
+
+void Q3ComboTableItem::setCurrentItem(int i)
+{
+ QWidget *w = table()->cellWidget(row(), col());
+ Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
+ if (cb) {
+ cb->setCurrentItem(i);
+ current = cb->currentItem();
+ setText(cb->currentText());
+ } else {
+ if (i < 0 || i >= entries.count())
+ return;
+ current = i;
+ setText(entries.at(i));
+ table()->updateCell(row(), col());
+ }
+}
+
+/*!
+ \overload
+
+ Sets the list item whose text is \a s to be the combo table item's
+ current list item. Does nothing if no list item has the text \a s.
+
+ \sa currentItem()
+*/
+
+void Q3ComboTableItem::setCurrentItem(const QString &s)
+{
+ int i = entries.findIndex(s);
+ if (i != -1)
+ setCurrentItem(i);
+}
+
+/*!
+ Returns the index of the combo table item's current list item.
+
+ \sa setCurrentItem()
+*/
+
+int Q3ComboTableItem::currentItem() const
+{
+ QWidget *w = table()->cellWidget(row(), col());
+ Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
+ if (cb)
+ return cb->currentItem();
+ return current;
+}
+
+/*!
+ Returns the text of the combo table item's current list item.
+
+ \sa currentItem() text()
+*/
+
+QString Q3ComboTableItem::currentText() const
+{
+ QWidget *w = table()->cellWidget(row(), col());
+ Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
+ if (cb)
+ return cb->currentText();
+ return entries.value(current);
+}
+
+/*!
+ Returns the total number of list items in the combo table item.
+*/
+
+int Q3ComboTableItem::count() const
+{
+ QWidget *w = table()->cellWidget(row(), col());
+ Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
+ if (cb)
+ return cb->count();
+ return (int)entries.count();
+}
+
+/*!
+ Returns the text of the combo's list item at index \a i.
+
+ \sa currentText()
+*/
+
+QString Q3ComboTableItem::text(int i) const
+{
+ QWidget *w = table()->cellWidget(row(), col());
+ Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w);
+ if (cb)
+ return cb->text(i);
+ return entries.value(i);
+}
+
+/*!
+ If \a b is true the combo table item can be edited, i.e. the user
+ may enter a new text item themselves. If \a b is false the user may
+ may only choose one of the existing items.
+
+ \sa isEditable()
+*/
+
+void Q3ComboTableItem::setEditable(bool b)
+{
+ edit = b;
+}
+
+/*!
+ Returns true if the user can add their own list items to the
+ combobox's list of items; otherwise returns false.
+
+ \sa setEditable()
+*/
+
+bool Q3ComboTableItem::isEditable() const
+{
+ return edit;
+}
+
+int Q3ComboTableItem::RTTI = 1;
+
+/*!
+ \fn int Q3ComboTableItem::rtti() const
+
+ Returns 1.
+
+ Make your derived classes return their own values for rtti()to
+ distinguish between different table item subclasses. You should
+ use values greater than 1000, preferably a large random number, to
+ allow for extensions to this class.
+
+
+ \sa Q3TableItem::rtti()
+*/
+
+int Q3ComboTableItem::rtti() const
+{
+ return RTTI;
+}
+
+/*! \reimp */
+
+QSize Q3ComboTableItem::sizeHint() const
+{
+ fakeCombo->insertItem(currentText());
+ fakeCombo->setCurrentItem(fakeCombo->count() - 1);
+ QSize sh = fakeCombo->sizeHint();
+ fakeCombo->removeItem(fakeCombo->count() - 1);
+ return sh.expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \fn QString Q3ComboTableItem::text() const
+
+ Returns the text of the table item or an empty string if there is
+ no text.
+
+ \sa Q3TableItem::text()
+*/
+
+/*!
+ \class Q3CheckTableItem
+ \brief The Q3CheckTableItem class provides checkboxes in Q3Tables.
+
+ \compat
+
+ A Q3CheckTableItem is a table item which looks and behaves like a
+ checkbox. The advantage of using Q3CheckTableItems rather than real
+ checkboxes is that a Q3CheckTableItem uses far less resources than
+ a real checkbox would in a \l{Q3Table}. When the cell has the focus
+ it displays a real checkbox which the user can interact with. When
+ the cell does not have the focus the cell \e looks like a
+ checkbox. Pixmaps may not be used in Q3CheckTableItems.
+
+ Q3CheckTableItem items have the edit type \c WhenCurrent (see
+ \l{EditType}).
+
+ To change the checkbox's label use setText(). The checkbox can be
+ checked and unchecked with setChecked() and its state retrieved
+ using isChecked().
+
+ To populate a table cell with a Q3CheckTableItem use
+ Q3Table::setItem().
+
+ Q3CheckTableItems can be distinguished from \l{Q3TableItem}s and
+ \l{Q3ComboTableItem}s using their Run Time Type Identification
+ (rtti) value.
+
+ \img qtableitems.png Table Items
+
+ \sa rtti() EditType Q3ComboTableItem Q3TableItem QCheckBox
+*/
+
+/*!
+ Creates a Q3CheckTableItem with an \l{EditType} of \c WhenCurrent
+ as a child of \a table. The checkbox is initially unchecked and
+ its label is set to the string \a txt.
+*/
+
+Q3CheckTableItem::Q3CheckTableItem(Q3Table *table, const QString &txt)
+ : Q3TableItem(table, WhenCurrent, txt), checked(false)
+{
+}
+
+/*! \reimp */
+
+void Q3CheckTableItem::setText(const QString &t)
+{
+ Q3TableItem::setText(t);
+ QWidget *w = table()->cellWidget(row(), col());
+ QCheckBox *cb = qobject_cast<QCheckBox*>(w);
+ if (cb)
+ cb->setText(t);
+}
+
+
+/*! \reimp */
+
+QWidget *Q3CheckTableItem::createEditor() const
+{
+ // create an editor - a combobox in our case
+ ((Q3CheckTableItem*)this)->cb = new QCheckBox(table()->viewport(), "qt_editor_checkbox");
+ cb->setChecked(checked);
+ cb->setText(text());
+ cb->setBackgroundColor(table()->viewport()->backgroundColor());
+ cb->setAutoFillBackground(true);
+ QObject::connect(cb, SIGNAL(toggled(bool)), table(), SLOT(doValueChanged()));
+ return cb;
+}
+
+/*! \reimp */
+
+void Q3CheckTableItem::setContentFromEditor(QWidget *w)
+{
+ QCheckBox *cb = qobject_cast<QCheckBox*>(w);
+ if (cb)
+ checked = cb->isChecked();
+}
+
+/*! \reimp */
+
+void Q3CheckTableItem::paint(QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected)
+{
+ QPalette pal = cg;
+
+ p->fillRect(0, 0, cr.width(), cr.height(),
+ selected ? pal.brush(QPalette::Highlight)
+ : pal.brush(QPalette::Base));
+
+ QSize sz = QSize(table()->style()->pixelMetric(QStyle::PM_IndicatorWidth),
+ table()->style()->pixelMetric(QStyle::PM_IndicatorHeight));
+ QPalette pal2(pal);
+ pal2.setBrush(QPalette::Window, pal.brush(QPalette::Base));
+ QStyleOptionButton opt;
+ opt.initFrom(table());
+ opt.rect.setRect(0, (cr.height() - sz.height()) / 2, sz.width(), sz.height());
+ opt.palette = pal2;
+ opt.state &= ~QStyle::State_HasFocus;
+ opt.state &= ~QStyle::State_MouseOver;
+ if(isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ if (checked)
+ opt.state |= QStyle::State_On;
+ else
+ opt.state |= QStyle::State_Off;
+ if (isEnabled() && table()->isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ table()->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, p, table());
+ if (selected)
+ p->setPen(pal.highlightedText().color());
+ else
+ p->setPen(pal.text().color());
+ opt.rect.setRect(0, 0, cr.width(), cr.height());
+ QRect textRect = table()->style()->subElementRect(QStyle::SE_CheckBoxContents, &opt, table());
+ p->drawText(textRect, wordWrap() ? (alignment() | Qt::WordBreak) : alignment(), text());
+}
+
+/*!
+ If \a b is true the checkbox is checked; if \a b is false the
+ checkbox is unchecked.
+
+ \sa isChecked()
+*/
+
+void Q3CheckTableItem::setChecked(bool b)
+{
+ checked = b;
+ table()->updateCell(row(), col());
+ QWidget *w = table()->cellWidget(row(), col());
+ QCheckBox *cb = qobject_cast<QCheckBox*>(w);
+ if (cb)
+ cb->setChecked(b);
+}
+
+/*!
+ Returns true if the checkbox table item is checked; otherwise
+ returns false.
+
+ \sa setChecked()
+*/
+
+bool Q3CheckTableItem::isChecked() const
+{
+ // #### why was this next line here. It must not be here, as
+ // #### people want to call isChecked() from within paintCell()
+ // #### and end up in an infinite loop that way
+ // table()->updateCell(row(), col());
+ QWidget *w = table()->cellWidget(row(), col());
+ QCheckBox *cb = qobject_cast<QCheckBox*>(w);
+ if (cb)
+ return cb->isChecked();
+ return checked;
+}
+
+int Q3CheckTableItem::RTTI = 2;
+
+/*!
+ \fn int Q3CheckTableItem::rtti() const
+
+ Returns 2.
+
+ Make your derived classes return their own values for rtti()to
+ distinguish between different table item subclasses. You should
+ use values greater than 1000, preferably a large random number, to
+ allow for extensions to this class.
+
+ \sa Q3TableItem::rtti()
+*/
+
+int Q3CheckTableItem::rtti() const
+{
+ return RTTI;
+}
+
+/*! \reimp */
+
+QSize Q3CheckTableItem::sizeHint() const
+{
+ QSize sz = QSize(table()->style()->pixelMetric(QStyle::PM_IndicatorWidth),
+ table()->style()->pixelMetric(QStyle::PM_IndicatorHeight));
+ sz.setWidth(sz.width() + 6);
+ QSize sh(Q3TableItem::sizeHint());
+ return QSize(sh.width() + sz.width(), QMAX(sh.height(), sz.height())).
+ expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \class Q3Table
+ \brief The Q3Table class provides a flexible editable table widget.
+
+ \compat
+
+ Q3Table is easy to use, although it does have a large API because
+ of the comprehensive functionality that it provides. Q3Table
+ includes functions for manipulating \link #headers
+ headers\endlink, \link #columnsrows rows and columns\endlink,
+ \link #cells cells\endlink and \link #selections
+ selections\endlink. Q3Table also provides in-place editing and
+ drag and drop, as well as a useful set of
+ \link #signals signals\endlink. Q3Table efficiently supports very
+ large tables, for example, tables one million by one million cells
+ are perfectly possible. Q3Table is economical with memory, using
+ none for unused cells.
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 3
+
+ The first line constructs the table specifying its size in rows
+ and columns. We then insert a pixmap and some text into the \e
+ same \link #cells cell\endlink, with the pixmap appearing to the
+ left of the text. Q3Table cells can be populated with
+ \l{Q3TableItem}s, \l{Q3ComboTableItem}s or by \l{Q3CheckTableItem}s.
+ By default a vertical header appears at the left of the table
+ showing row numbers and a horizontal header appears at the top of
+ the table showing column numbers. (The numbers displayed start at
+ 1, although row and column numbers within Q3Table begin at 0.)
+
+ If you want to use mouse tracking call setMouseTracking(true) on
+ the \e viewport.
+
+ \img qtableitems.png Table Items
+
+ \target headers
+ \section1 Headers
+
+ Q3Table supports a header column, e.g. to display row numbers, and
+ a header row, e.g to display column titles. To set row or column
+ labels use Q3Header::setLabel() on the pointers returned by
+ verticalHeader() and horizontalHeader() respectively. The vertical
+ header is displayed within the table's left margin whose width is
+ set with setLeftMargin(). The horizontal header is displayed
+ within the table's top margin whose height is set with
+ setTopMargin(). The table's grid can be switched off with
+ setShowGrid(). If you want to hide a horizontal header call
+ hide(), and call setTopMargin(0) so that the area the header
+ would have occupied is reduced to zero size.
+
+ Header labels are indexed via their section numbers. Note that the
+ default behavior of Q3Header regarding section numbers is overridden
+ for Q3Table. See the explanation below in the Rows and Columns
+ section in the discussion of moving columns and rows.
+
+ \target columnsrows
+ \section1 Rows and Columns
+
+ Row and column sizes are set with setRowHeight() and
+ setColumnWidth(). If you want a row high enough to show the
+ tallest item in its entirety, use adjustRow(). Similarly, to make
+ a column wide enough to show the widest item use adjustColumn().
+ If you want the row height and column width to adjust
+ automatically as the height and width of the table changes use
+ setRowStretchable() and setColumnStretchable().
+
+ Rows and columns can be hidden and shown with hideRow(),
+ hideColumn(), showRow() and showColumn(). New rows and columns are
+ inserted using insertRows() and insertColumns(). Additional rows
+ and columns are added at the bottom (rows) or right (columns) if
+ you set setNumRows() or setNumCols() to be larger than numRows()
+ or numCols(). Existing rows and columns are removed with
+ removeRow() and removeColumn(). Multiple rows and columns can be
+ removed with removeRows() and removeColumns().
+
+ Rows and columns can be set to be movable using
+ rowMovingEnabled() and columnMovingEnabled(). The user can drag
+ them to reorder them holding down the Ctrl key and dragging the
+ mouse. For performance reasons, the default behavior of Q3Header
+ section numbers is overridden by Q3Table. Currently in Q3Table, when
+ a row or column is dragged and reordered, the section number is
+ also changed to its new position. Therefore, there is no
+ difference between the section and the index fields in Q3Header.
+ The Q3Table Q3Header classes do not provide a mechanism for indexing
+ independently of the user interface ordering.
+
+ The table can be sorted using sortColumn(). Users can sort a
+ column by clicking its header if setSorting() is set to true. Rows
+ can be swapped with swapRows(), columns with swapColumns() and
+ cells with swapCells().
+
+ For editable tables (see setReadOnly()) you can set the read-only
+ property of individual rows and columns with setRowReadOnly() and
+ setColumnReadOnly(). (Whether a cell is editable or read-only
+ depends on these settings and the cell's Q3TableItem.
+
+ The row and column which have the focus are returned by
+ currentRow() and currentColumn() respectively.
+
+ Although many Q3Table functions operate in terms of rows and
+ columns the indexOf() function returns a single integer
+ identifying a particular cell.
+
+ \target cells
+ \section1 Cells
+
+ All of a Q3Table's cells are empty when the table is constructed.
+
+ There are two approaches to populating the table's cells. The
+ first and simplest approach is to use Q3TableItems or Q3TableItem
+ subclasses. The second approach doesn't use Q3TableItems at all
+ which is useful for very large sparse tables but requires you to
+ reimplement a number of functions. We'll look at each approach in
+ turn.
+
+ To put a string in a cell use setText(). This function will create
+ a new Q3TableItem for the cell if one doesn't already exist, and
+ displays the text in it. By default the table item's widget will
+ be a QLineEdit. A pixmap may be put in a cell with setPixmap(),
+ which also creates a table item if required. A cell may contain \e
+ both a pixmap and text; the pixmap is displayed to the left of the
+ text. Another approach is to construct a Q3TableItem or Q3TableItem
+ subclass, set its properties, then insert it into a cell with
+ setItem().
+
+ If you want cells which contain comboboxes use the Q3ComboTableItem
+ class. Similarly if you require cells containing checkboxes use
+ the Q3CheckTableItem class. These table items look and behave just
+ like the combobox or checkbox widgets but consume far less memory.
+
+ Q3Table takes ownership of its Q3TableItems and will delete them
+ when the table itself is destroyed. You can take ownership of a
+ table item using takeItem() which you use to move a cell's
+ contents from one cell to another, either within the same table,
+ or from one table to another. (See also, swapCells()).
+
+ In-place editing of the text in Q3TableItems, and the values in
+ Q3ComboTableItems and Q3CheckTableItems works automatically. Cells
+ may be editable or read-only, see Q3TableItem::EditType. If you
+ want fine control over editing see beginEdit() and endEdit().
+
+ The contents of a cell can be retrieved as a Q3TableItem using
+ item(), or as a string with text() or as a pixmap (if there is
+ one) with pixmap(). A cell's bounding rectangle is given by
+ cellGeometry(). Use updateCell() to repaint a cell, for example to
+ clear away a cell's visual representation after it has been
+ deleted with clearCell(). The table can be forced to scroll to
+ show a particular cell with ensureCellVisible(). The isSelected()
+ function indicates if a cell is selected.
+
+ It is possible to use your own widget as a cell's widget using
+ setCellWidget(), but subclassing Q3TableItem might be a simpler
+ approach. The cell's widget (if there is one) can be removed with
+ clearCellWidget().
+
+ \keyword notes on large tables
+ \target bigtables
+ \section2 Large tables
+
+ For large, sparse, tables using Q3TableItems or other widgets is
+ inefficient. The solution is to \e draw the cell as it should
+ appear and to create and destroy cell editors on demand.
+
+ This approach requires that you reimplement various functions.
+ Reimplement paintCell() to display your data, and createEditor()
+ and setCellContentFromEditor() to support in-place editing. It
+ is very important to reimplement resizeData() to have no
+ functionality, to prevent Q3Table from attempting to create a huge
+ array. You will also need to reimplement item(), setItem(),
+ takeItem(), clearCell(), and insertWidget(), cellWidget() and
+ clearCellWidget(). In almost every circumstance (for sorting,
+ removing and inserting columns and rows, etc.), you also need
+ to reimplement swapRows(), swapCells() and swapColumns(), including
+ header handling.
+
+ If you represent active cells with a dictionary of Q3TableItems and
+ QWidgets, i.e. only store references to cells that are actually
+ used, many of the functions can be implemented with a single line
+ of code.
+
+ For more information on cells see the Q3TableItem documenation.
+
+ \target selections
+ \section1 Selections
+
+ Q3Table's support single selection, multi-selection (multiple
+ cells) or no selection. The selection mode is set with
+ setSelectionMode(). Use isSelected() to determine if a particular
+ cell is selected, and isRowSelected() and isColumnSelected() to
+ see if a row or column is selected.
+
+ Q3Table's support many simultaneous selections. You can
+ programmatically select cells with addSelection(). The number of
+ selections is given by numSelections(). The current selection is
+ returned by currentSelection(). You can remove a selection with
+ removeSelection() and remove all selections with
+ clearSelection(). Selections are Q3TableSelection objects.
+
+ To easily add a new selection use selectCells(), selectRow() or
+ selectColumn().
+
+ Alternatively, use addSelection() to add new selections using
+ Q3TableSelection objects. The advantage of using Q3TableSelection
+ objects is that you can call Q3TableSelection::expandTo() to resize
+ the selection and can query and compare them.
+
+ The number of selections is given by numSelections(). The current
+ selection is returned by currentSelection(). You can remove a
+ selection with removeSelection() and remove all selections with
+ clearSelection().
+
+ \target signals
+ \section1 Signals
+
+ When the user clicks a cell the currentChanged() signal is
+ emitted. You can also connect to the lower level clicked(),
+ doubleClicked() and pressed() signals. If the user changes the
+ selection the selectionChanged() signal is emitted; similarly if
+ the user changes a cell's value the valueChanged() signal is
+ emitted. If the user right-clicks (or presses the appropriate
+ platform-specific key sequence) the contextMenuRequested() signal
+ is emitted. If the user drops a drag and drop object the dropped()
+ signal is emitted with the drop event.
+*/
+
+/*!
+ \fn void Q3Table::currentChanged(int row, int col)
+
+ This signal is emitted when the current cell has changed to \a
+ row, \a col.
+*/
+
+/*!
+ \fn void Q3Table::valueChanged(int row, int col)
+
+ This signal is emitted when the user changed the value in the cell
+ at \a row, \a col.
+*/
+
+/*!
+ \fn int Q3Table::currentRow() const
+
+ Returns the current row.
+
+ \sa currentColumn()
+*/
+
+/*!
+ \fn int Q3Table::currentColumn() const
+
+ Returns the current column.
+
+ \sa currentRow()
+*/
+
+/*!
+ \enum Q3Table::EditMode
+
+ \value NotEditing No cell is currently being edited.
+
+ \value Editing A cell is currently being edited. The editor was
+ initialised with the cell's contents.
+
+ \value Replacing A cell is currently being edited. The editor was
+ not initialised with the cell's contents.
+*/
+
+/*!
+ \enum Q3Table::SelectionMode
+
+ \value NoSelection No cell can be selected by the user.
+
+ \value Single The user may only select a single range of cells.
+
+ \value Multi The user may select multiple ranges of cells.
+
+ \value SingleRow The user may select one row at once.
+
+ \value MultiRow The user may select multiple rows.
+*/
+
+/*!
+ \enum Q3Table::FocusStyle
+
+ Specifies how the current cell (focus cell) is drawn.
+
+ \value FollowStyle The current cell is drawn according to the
+ current style and the cell's background is also drawn selected, if
+ the current cell is within a selection
+
+ \value SpreadSheet The current cell is drawn as in a spreadsheet.
+ This means, it is signified by a black rectangle around the cell,
+ and the background of the current cell is always drawn with the
+ widget's base color - even when selected.
+
+*/
+
+/*!
+ \fn void Q3Table::clicked(int row, int col, int button, const QPoint &mousePos)
+
+ This signal is emitted when mouse button \a button is clicked. The
+ cell where the event took place is at \a row, \a col, and the
+ mouse's position is in \a mousePos.
+
+ \sa Qt::MouseButton
+*/
+
+/*!
+ \fn void Q3Table::doubleClicked(int row, int col, int button, const QPoint &mousePos)
+
+ This signal is emitted when mouse button \a button is
+ double-clicked. The cell where the event took place is at \a row,
+ \a col, and the mouse's position is in \a mousePos.
+
+ \sa Qt::MouseButton
+*/
+
+/*!
+ \fn void Q3Table::pressed(int row, int col, int button, const QPoint &mousePos)
+
+ This signal is emitted when mouse button \a button is pressed. The
+ cell where the event took place is at \a row, \a col, and the
+ mouse's position is in \a mousePos.
+
+ \sa Qt::MouseButton
+*/
+
+/*!
+ \fn void Q3Table::selectionChanged()
+
+ This signal is emitted whenever a selection changes.
+
+ \sa Q3TableSelection
+*/
+
+/*!
+ \fn void Q3Table::contextMenuRequested(int row, int col, const QPoint & pos)
+
+ This signal is emitted when the user invokes a context menu with
+ the right mouse button (or with a system-specific keypress). The
+ cell where the event took place is at \a row, \a col. \a pos is
+ the position where the context menu will appear in the global
+ coordinate system. This signal is always emitted, even if the
+ contents of the cell are disabled.
+*/
+
+/*!
+ Creates an empty table object called \a name as a child of \a
+ parent.
+
+ Call setNumRows() and setNumCols() to set the table size before
+ populating the table if you're using Q3TableItems.
+*/
+
+Q3Table::Q3Table(QWidget *parent, const char *name)
+ : Q3ScrollView(parent, name, WNoAutoErase | WStaticContents),
+ leftHeader(0), topHeader(0),
+ currentSel(0), lastSortCol(-1), sGrid(true), mRows(false), mCols(false),
+ asc(true), doSort(true), readOnly(false)
+{
+ init(0, 0);
+}
+
+/*!
+ Constructs an empty table called \a name with \a numRows rows and
+ \a numCols columns. The table is a child of \a parent.
+
+ If you're using \l{Q3TableItem}s to populate the table's cells, you
+ can create Q3TableItem, Q3ComboTableItem and Q3CheckTableItem items
+ and insert them into the table using setItem(). (See the notes on
+ large tables for an alternative to using Q3TableItems.)
+*/
+
+Q3Table::Q3Table(int numRows, int numCols, QWidget *parent, const char *name)
+ : Q3ScrollView(parent, name, WNoAutoErase | WStaticContents),
+ leftHeader(0), topHeader(0),
+ currentSel(0), lastSortCol(-1), sGrid(true), mRows(false), mCols(false),
+ asc(true), doSort(true), readOnly(false)
+{
+ init(numRows, numCols);
+}
+
+/*! \internal
+*/
+
+void Q3Table::init(int rows, int cols)
+{
+#ifndef QT_NO_DRAGANDDROP
+ setDragAutoScroll(false);
+#endif
+ d = new Q3TablePrivate;
+ d->geomTimer = new QTimer(this);
+ d->lastVisCol = 0;
+ d->lastVisRow = 0;
+ connect(d->geomTimer, SIGNAL(timeout()), this, SLOT(updateGeometriesSlot()));
+ shouldClearSelection = false;
+ dEnabled = false;
+ roRows.setAutoDelete(true);
+ roCols.setAutoDelete(true);
+ setSorting(false);
+
+ unused = true; // It's unused, ain't it? :)
+
+ selMode = Multi;
+
+ contents.setAutoDelete(true);
+ widgets.setAutoDelete(true);
+
+ // Enable clipper and set background mode
+ enableClipper(qt_table_clipper_enabled);
+
+ viewport()->setFocusProxy(this);
+ viewport()->setFocusPolicy(Qt::WheelFocus);
+ setFocusPolicy(Qt::WheelFocus);
+
+ viewport()->setBackgroundMode(PaletteBase);
+ setBackgroundMode(PaletteBackground, PaletteBase);
+ setResizePolicy(Manual);
+ selections.setAutoDelete(true);
+
+ // Create headers
+ leftHeader = new Q3TableHeader(rows, this, this, "left table header");
+ leftHeader->setOrientation(Vertical);
+ leftHeader->setTracking(true);
+ leftHeader->setMovingEnabled(true);
+ topHeader = new Q3TableHeader(cols, this, this, "right table header");
+ topHeader->setOrientation(Horizontal);
+ topHeader->setTracking(true);
+ topHeader->setMovingEnabled(true);
+ if (QApplication::reverseLayout())
+ setMargins(0, fontMetrics().height() + 4, 30, 0);
+ else
+ setMargins(30, fontMetrics().height() + 4, 0, 0);
+
+ topHeader->setUpdatesEnabled(false);
+ leftHeader->setUpdatesEnabled(false);
+ // Initialize headers
+ int i = 0;
+ for (i = 0; i < numCols(); ++i)
+ topHeader->resizeSection(i, QMAX(100, QApplication::globalStrut().height()));
+ for (i = 0; i < numRows(); ++i)
+ leftHeader->resizeSection(i, QMAX(20, QApplication::globalStrut().width()));
+ topHeader->setUpdatesEnabled(true);
+ leftHeader->setUpdatesEnabled(true);
+
+ // Prepare for contents
+ contents.setAutoDelete(false);
+
+ // Connect header, table and scroll bars
+ connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ topHeader, SLOT(setOffset(int)));
+ connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
+ leftHeader, SLOT(setOffset(int)));
+ connect(topHeader, SIGNAL(sectionSizeChanged(int)),
+ this, SLOT(columnWidthChanged(int)));
+ connect(topHeader, SIGNAL(indexChange(int,int,int)),
+ this, SLOT(columnIndexChanged(int,int,int)));
+ connect(topHeader, SIGNAL(sectionClicked(int)),
+ this, SLOT(columnClicked(int)));
+ connect(leftHeader, SIGNAL(sectionSizeChanged(int)),
+ this, SLOT(rowHeightChanged(int)));
+ connect(leftHeader, SIGNAL(indexChange(int,int,int)),
+ this, SLOT(rowIndexChanged(int,int,int)));
+
+ // Initialize variables
+ autoScrollTimer = new QTimer(this);
+ connect(autoScrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+ curRow = curCol = 0;
+ topHeader->setSectionState(curCol, Q3TableHeader::Bold);
+ leftHeader->setSectionState(curRow, Q3TableHeader::Bold);
+ edMode = NotEditing;
+ editRow = editCol = -1;
+
+ drawActiveSelection = true;
+
+ installEventFilter(this);
+
+ focusStl = SpreadSheet;
+
+ was_visible = false;
+
+ // initial size
+ resize(640, 480);
+}
+
+/*!
+ Releases all the resources used by the Q3Table object,
+ including all \l{Q3TableItem}s and their widgets.
+*/
+
+Q3Table::~Q3Table()
+{
+ setUpdatesEnabled(false);
+ contents.setAutoDelete(true);
+ contents.clear();
+ widgets.clear();
+
+ delete d;
+}
+
+void Q3Table::setReadOnly(bool b)
+{
+ readOnly = b;
+
+ Q3TableItem *i = item(curRow, curCol);
+ if (readOnly && isEditing()) {
+ endEdit(editRow, editCol, true, false);
+ } else if (!readOnly && i && (i->editType() == Q3TableItem::WhenCurrent
+ || i->editType() == Q3TableItem::Always)) {
+ editCell(curRow, curCol);
+ }
+}
+
+/*!
+ If \a ro is true, row \a row is set to be read-only; otherwise the
+ row is set to be editable.
+
+ Whether a cell in this row is editable or read-only depends on the
+ cell's EditType, and this setting.
+
+ \sa isRowReadOnly() setColumnReadOnly() setReadOnly()
+*/
+
+void Q3Table::setRowReadOnly(int row, bool ro)
+{
+ if (ro)
+ roRows.replace(row, new int(0));
+ else
+ roRows.remove(row);
+
+ if (curRow == row) {
+ Q3TableItem *i = item(curRow, curCol);
+ if (ro && isEditing()) {
+ endEdit(editRow, editCol, true, false);
+ } else if (!ro && i && (i->editType() == Q3TableItem::WhenCurrent
+ || i->editType() == Q3TableItem::Always)) {
+ editCell(curRow, curCol);
+ }
+ }
+}
+
+/*!
+ If \a ro is true, column \a col is set to be read-only; otherwise
+ the column is set to be editable.
+
+ Whether a cell in this column is editable or read-only depends on
+ the cell's EditType, and this setting.
+
+ \sa isColumnReadOnly() setRowReadOnly() setReadOnly()
+
+*/
+
+void Q3Table::setColumnReadOnly(int col, bool ro)
+{
+ if (ro)
+ roCols.replace(col, new int(0));
+ else
+ roCols.remove(col);
+
+ if (curCol == col) {
+ Q3TableItem *i = item(curRow, curCol);
+ if (ro && isEditing()) {
+ endEdit(editRow, editCol, true, false);
+ } else if (!ro && i && (i->editType() == Q3TableItem::WhenCurrent
+ || i->editType() == Q3TableItem::Always)) {
+ editCell(curRow, curCol);
+ }
+ }
+}
+
+/*!
+ \property Q3Table::readOnly
+ \brief whether the table is read-only
+
+ Whether a cell in the table is editable or read-only depends on
+ the cell's \link Q3TableItem::EditType EditType\endlink, and this setting.
+
+ \sa QWidget::enabled setColumnReadOnly() setRowReadOnly()
+*/
+
+bool Q3Table::isReadOnly() const
+{
+ return readOnly;
+}
+
+/*!
+ Returns true if row \a row is read-only; otherwise returns false.
+
+ Whether a cell in this row is editable or read-only depends on the
+ cell's \link Q3TableItem::EditType EditType\endlink, and this
+ setting.
+
+ \sa setRowReadOnly() isColumnReadOnly()
+*/
+
+bool Q3Table::isRowReadOnly(int row) const
+{
+ return (roRows.find(row) != 0);
+}
+
+/*!
+ Returns true if column \a col is read-only; otherwise returns
+ false.
+
+ Whether a cell in this column is editable or read-only depends on
+ the cell's EditType, and this setting.
+
+ \sa setColumnReadOnly() isRowReadOnly()
+*/
+
+bool Q3Table::isColumnReadOnly(int col) const
+{
+ return (roCols.find(col) != 0);
+}
+
+void Q3Table::setSelectionMode(SelectionMode mode)
+{
+ if (mode == selMode)
+ return;
+ selMode = mode;
+ clearSelection();
+ if (isRowSelection(selMode) && numRows() > 0 && numCols() > 0) {
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ currentSel->init(curRow, 0);
+ currentSel->expandTo(curRow, numCols() - 1);
+ repaintSelections(0, currentSel);
+ }
+}
+
+/*!
+ \property Q3Table::selectionMode
+ \brief the current selection mode
+
+ The default mode is \c Multi which allows the user to select
+ multiple ranges of cells.
+*/
+
+Q3Table::SelectionMode Q3Table::selectionMode() const
+{
+ return selMode;
+}
+
+/*!
+ \property Q3Table::focusStyle
+ \brief how the current (focus) cell is drawn
+
+ The default style is \c SpreadSheet.
+
+ \sa Q3Table::FocusStyle
+*/
+
+void Q3Table::setFocusStyle(FocusStyle fs)
+{
+ focusStl = fs;
+ updateCell(curRow, curCol);
+}
+
+Q3Table::FocusStyle Q3Table::focusStyle() const
+{
+ return focusStl;
+}
+
+/*!
+ This functions updates all the header states to be in sync with
+ the current selections. This should be called after
+ programmatically changing, adding or removing selections, so that
+ the headers are updated.
+*/
+
+void Q3Table::updateHeaderStates()
+{
+ horizontalHeader()->setUpdatesEnabled(false);
+ verticalHeader()->setUpdatesEnabled(false);
+
+ ((Q3TableHeader*)verticalHeader())->setSectionStateToAll(Q3TableHeader::Normal);
+ ((Q3TableHeader*)horizontalHeader())->setSectionStateToAll(Q3TableHeader::Normal);
+
+ Q3PtrListIterator<Q3TableSelection> it(selections);
+ Q3TableSelection *s;
+ while ((s = it.current()) != 0) {
+ ++it;
+ if (s->isActive()) {
+ if (s->leftCol() == 0 &&
+ s->rightCol() == numCols() - 1) {
+ for (int i = 0; i < s->bottomRow() - s->topRow() + 1; ++i)
+ leftHeader->setSectionState(s->topRow() + i, Q3TableHeader::Selected);
+ }
+ if (s->topRow() == 0 &&
+ s->bottomRow() == numRows() - 1) {
+ for (int i = 0; i < s->rightCol() - s->leftCol() + 1; ++i)
+ topHeader->setSectionState(s->leftCol() + i, Q3TableHeader::Selected);
+ }
+ }
+ }
+
+ horizontalHeader()->setUpdatesEnabled(true);
+ verticalHeader()->setUpdatesEnabled(true);
+ horizontalHeader()->repaint(false);
+ verticalHeader()->repaint(false);
+}
+
+/*!
+ Returns the table's top Q3Header.
+
+ This header contains the column labels.
+
+ To modify a column label use Q3Header::setLabel().
+
+ \sa verticalHeader() setTopMargin() Q3Header
+*/
+
+Q3Header *Q3Table::horizontalHeader() const
+{
+ return (Q3Header*)topHeader;
+}
+
+/*!
+ Returns the table's vertical Q3Header.
+
+ This header contains the row labels.
+
+ \sa horizontalHeader() setLeftMargin() Q3Header
+*/
+
+Q3Header *Q3Table::verticalHeader() const
+{
+ return (Q3Header*)leftHeader;
+}
+
+void Q3Table::setShowGrid(bool b)
+{
+ if (sGrid == b)
+ return;
+ sGrid = b;
+ updateContents();
+}
+
+/*!
+ \property Q3Table::showGrid
+ \brief whether the table's grid is displayed
+
+ The grid is shown by default.
+*/
+
+bool Q3Table::showGrid() const
+{
+ return sGrid;
+}
+
+/*!
+ \property Q3Table::columnMovingEnabled
+ \brief whether columns can be moved by the user
+
+ The default is false. Columns are moved by dragging whilst holding
+ down the Ctrl key.
+
+ \sa rowMovingEnabled
+*/
+
+void Q3Table::setColumnMovingEnabled(bool b)
+{
+ mCols = b;
+}
+
+bool Q3Table::columnMovingEnabled() const
+{
+ return mCols;
+}
+
+/*!
+ \property Q3Table::rowMovingEnabled
+ \brief whether rows can be moved by the user
+
+ The default is false. Rows are moved by dragging whilst holding
+ down the Ctrl key.
+
+
+ \sa columnMovingEnabled
+*/
+
+void Q3Table::setRowMovingEnabled(bool b)
+{
+ mRows = b;
+}
+
+bool Q3Table::rowMovingEnabled() const
+{
+ return mRows;
+}
+
+/*!
+ This is called when Q3Table's internal array needs to be resized to
+ \a len elements.
+
+ If you don't use Q3TableItems you should reimplement this as an
+ empty method to avoid wasting memory. See the notes on large
+ tables for further details.
+*/
+
+void Q3Table::resizeData(int len)
+{
+ contents.resize(len);
+ widgets.resize(len);
+}
+
+/*!
+ Swaps the data in \a row1 and \a row2.
+
+ This function is used to swap the positions of two rows. It is
+ called when the user changes the order of rows (see
+ setRowMovingEnabled()), and when rows are sorted.
+
+ If you don't use \l{Q3TableItem}s and want your users to be able to
+ swap rows, e.g. for sorting, you will need to reimplement this
+ function. (See the notes on large tables.)
+
+ If \a swapHeader is true, the rows' header contents is also
+ swapped.
+
+ This function will not update the Q3Table, you will have to do
+ this manually, e.g. by calling updateContents().
+
+ \sa swapColumns() swapCells()
+*/
+
+void Q3Table::swapRows(int row1, int row2, bool swapHeader)
+{
+ if (swapHeader)
+ leftHeader->swapSections(row1, row2, false);
+
+ Q3PtrVector<Q3TableItem> tmpContents;
+ tmpContents.resize(numCols());
+ Q3PtrVector<QWidget> tmpWidgets;
+ tmpWidgets.resize(numCols());
+ int i;
+
+ contents.setAutoDelete(false);
+ widgets.setAutoDelete(false);
+ for (i = 0; i < numCols(); ++i) {
+ Q3TableItem *i1, *i2;
+ i1 = item(row1, i);
+ i2 = item(row2, i);
+ if (i1 || i2) {
+ tmpContents.insert(i, i1);
+ contents.remove(indexOf(row1, i));
+ contents.insert(indexOf(row1, i), i2);
+ contents.remove(indexOf(row2, i));
+ contents.insert(indexOf(row2, i), tmpContents[ i ]);
+ if (contents[ indexOf(row1, i) ])
+ contents[ indexOf(row1, i) ]->setRow(row1);
+ if (contents[ indexOf(row2, i) ])
+ contents[ indexOf(row2, i) ]->setRow(row2);
+ }
+
+ QWidget *w1, *w2;
+ w1 = cellWidget(row1, i);
+ w2 = cellWidget(row2, i);
+ if (w1 || w2) {
+ tmpWidgets.insert(i, w1);
+ widgets.remove(indexOf(row1, i));
+ widgets.insert(indexOf(row1, i), w2);
+ widgets.remove(indexOf(row2, i));
+ widgets.insert(indexOf(row2, i), tmpWidgets[ i ]);
+ }
+ }
+ contents.setAutoDelete(false);
+ widgets.setAutoDelete(true);
+
+ updateRowWidgets(row1);
+ updateRowWidgets(row2);
+ if (curRow == row1)
+ curRow = row2;
+ else if (curRow == row2)
+ curRow = row1;
+ if (editRow == row1)
+ editRow = row2;
+ else if (editRow == row2)
+ editRow = row1;
+}
+
+/*!
+ Sets the left margin to be \a m pixels wide.
+
+ The verticalHeader(), which displays row labels, occupies this
+ margin.
+
+ In an Arabic or Hebrew localization, the verticalHeader() will
+ appear on the right side of the table, and this call will set the
+ right margin.
+
+ \sa leftMargin() setTopMargin() verticalHeader()
+*/
+
+void Q3Table::setLeftMargin(int m)
+{
+ if (QApplication::reverseLayout())
+ setMargins(leftMargin(), topMargin(), m, bottomMargin());
+ else
+ setMargins(m, topMargin(), rightMargin(), bottomMargin());
+ updateGeometries();
+}
+
+/*!
+ Sets the top margin to be \a m pixels high.
+
+ The horizontalHeader(), which displays column labels, occupies
+ this margin.
+
+ \sa topMargin() setLeftMargin()
+*/
+
+void Q3Table::setTopMargin(int m)
+{
+ setMargins(leftMargin(), m, rightMargin(), bottomMargin());
+ updateGeometries();
+}
+
+/*!
+ Swaps the data in \a col1 with \a col2.
+
+ This function is used to swap the positions of two columns. It is
+ called when the user changes the order of columns (see
+ setColumnMovingEnabled(), and when columns are sorted.
+
+ If you don't use \l{Q3TableItem}s and want your users to be able to
+ swap columns you will need to reimplement this function. (See the
+ notes on large tables.)
+
+ If \a swapHeader is true, the columns' header contents is also
+ swapped.
+
+ \sa swapCells()
+*/
+
+void Q3Table::swapColumns(int col1, int col2, bool swapHeader)
+{
+ if (swapHeader)
+ topHeader->swapSections(col1, col2, false);
+
+ Q3PtrVector<Q3TableItem> tmpContents;
+ tmpContents.resize(numRows());
+ Q3PtrVector<QWidget> tmpWidgets;
+ tmpWidgets.resize(numRows());
+ int i;
+
+ contents.setAutoDelete(false);
+ widgets.setAutoDelete(false);
+ for (i = 0; i < numRows(); ++i) {
+ Q3TableItem *i1, *i2;
+ i1 = item(i, col1);
+ i2 = item(i, col2);
+ if (i1 || i2) {
+ tmpContents.insert(i, i1);
+ contents.remove(indexOf(i, col1));
+ contents.insert(indexOf(i, col1), i2);
+ contents.remove(indexOf(i, col2));
+ contents.insert(indexOf(i, col2), tmpContents[ i ]);
+ if (contents[ indexOf(i, col1) ])
+ contents[ indexOf(i, col1) ]->setCol(col1);
+ if (contents[ indexOf(i, col2) ])
+ contents[ indexOf(i, col2) ]->setCol(col2);
+ }
+
+ QWidget *w1, *w2;
+ w1 = cellWidget(i, col1);
+ w2 = cellWidget(i, col2);
+ if (w1 || w2) {
+ tmpWidgets.insert(i, w1);
+ widgets.remove(indexOf(i, col1));
+ widgets.insert(indexOf(i, col1), w2);
+ widgets.remove(indexOf(i, col2));
+ widgets.insert(indexOf(i, col2), tmpWidgets[ i ]);
+ }
+ }
+ contents.setAutoDelete(false);
+ widgets.setAutoDelete(true);
+
+ columnWidthChanged(col1);
+ columnWidthChanged(col2);
+ if (curCol == col1)
+ curCol = col2;
+ else if (curCol == col2)
+ curCol = col1;
+ if (editCol == col1)
+ editCol = col2;
+ else if (editCol == col2)
+ editCol = col1;
+}
+
+/*!
+ Swaps the contents of the cell at \a row1, \a col1 with the
+ contents of the cell at \a row2, \a col2.
+
+ This function is also called when the table is sorted.
+
+ If you don't use \l{Q3TableItem}s and want your users to be able to
+ swap cells, you will need to reimplement this function. (See the
+ notes on large tables.)
+
+ \sa swapColumns() swapRows()
+*/
+
+void Q3Table::swapCells(int row1, int col1, int row2, int col2)
+{
+ contents.setAutoDelete(false);
+ widgets.setAutoDelete(false);
+ Q3TableItem *i1, *i2;
+ i1 = item(row1, col1);
+ i2 = item(row2, col2);
+ if (i1 || i2) {
+ Q3TableItem *tmp = i1;
+ contents.remove(indexOf(row1, col1));
+ contents.insert(indexOf(row1, col1), i2);
+ contents.remove(indexOf(row2, col2));
+ contents.insert(indexOf(row2, col2), tmp);
+ if (contents[ indexOf(row1, col1) ]) {
+ contents[ indexOf(row1, col1) ]->setRow(row1);
+ contents[ indexOf(row1, col1) ]->setCol(col1);
+ }
+ if (contents[ indexOf(row2, col2) ]) {
+ contents[ indexOf(row2, col2) ]->setRow(row2);
+ contents[ indexOf(row2, col2) ]->setCol(col2);
+ }
+ }
+
+ QWidget *w1, *w2;
+ w1 = cellWidget(row1, col1);
+ w2 = cellWidget(row2, col2);
+ if (w1 || w2) {
+ QWidget *tmp = w1;
+ widgets.remove(indexOf(row1, col1));
+ widgets.insert(indexOf(row1, col1), w2);
+ widgets.remove(indexOf(row2, col2));
+ widgets.insert(indexOf(row2, col2), tmp);
+ }
+
+ updateRowWidgets(row1);
+ updateRowWidgets(row2);
+ updateColWidgets(col1);
+ updateColWidgets(col2);
+ contents.setAutoDelete(false);
+ widgets.setAutoDelete(true);
+}
+
+static bool is_child_of(QWidget *child, QWidget *parent)
+{
+ while (child) {
+ if (child == parent)
+ return true;
+ child = child->parentWidget();
+ }
+ return false;
+}
+
+/*!
+ Draws the table contents on the painter \a p. This function is
+ optimized so that it only draws the cells inside the \a cw pixels
+ wide and \a ch pixels high clipping rectangle at position \a cx,
+ \a cy.
+
+ Additionally, drawContents() highlights the current cell.
+*/
+
+void Q3Table::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ int colfirst = columnAt(cx);
+ int collast = columnAt(cx + cw);
+ int rowfirst = rowAt(cy);
+ int rowlast = rowAt(cy + ch);
+
+ if (rowfirst == -1 || colfirst == -1) {
+ paintEmptyArea(p, cx, cy, cw, ch);
+ return;
+ }
+
+ drawActiveSelection = hasFocus() || viewport()->hasFocus() || d->inMenuMode
+ || is_child_of(qApp->focusWidget(), viewport())
+ || !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this);
+ if (rowlast == -1)
+ rowlast = numRows() - 1;
+ if (collast == -1)
+ collast = numCols() - 1;
+
+ bool currentInSelection = false;
+
+ Q3PtrListIterator<Q3TableSelection> it( selections );
+ Q3TableSelection *s;
+ while ( ( s = it.current() ) != 0 ) {
+ ++it;
+ if (s->isActive() &&
+ curRow >= s->topRow() &&
+ curRow <= s->bottomRow() &&
+ curCol >= s->leftCol() &&
+ curCol <= s->rightCol()) {
+ currentInSelection = s->topRow() != curRow || s->bottomRow() != curRow || s->leftCol() != curCol || s->rightCol() != curCol;
+ break;
+ }
+ }
+
+ // Go through the rows
+ for (int r = rowfirst; r <= rowlast; ++r) {
+ // get row position and height
+ int rowp = rowPos(r);
+ int rowh = rowHeight(r);
+
+ // Go through the columns in row r
+ // if we know from where to where, go through [colfirst, collast],
+ // else go through all of them
+ for (int c = colfirst; c <= collast; ++c) {
+ // get position and width of column c
+ int colp, colw;
+ colp = columnPos(c);
+ colw = columnWidth(c);
+ int oldrp = rowp;
+ int oldrh = rowh;
+
+ Q3TableItem *itm = item(r, c);
+ if (itm &&
+ (itm->colSpan() > 1 || itm->rowSpan() > 1)) {
+ bool goon = (r == itm->row() && c == itm->col())
+ || (r == rowfirst && c == itm->col())
+ || (r == itm->row() && c == colfirst);
+ if (!goon)
+ continue;
+ rowp = rowPos(itm->row());
+ rowh = 0;
+ int i;
+ for (i = 0; i < itm->rowSpan(); ++i)
+ rowh += rowHeight(i + itm->row());
+ colp = columnPos(itm->col());
+ colw = 0;
+ for (i = 0; i < itm->colSpan(); ++i)
+ colw += columnWidth(i + itm->col());
+ }
+
+ // Translate painter and draw the cell
+ p->translate(colp, rowp);
+ bool selected = isSelected(r, c);
+ if (focusStl != FollowStyle && selected && !currentInSelection &&
+ r == curRow && c == curCol )
+ selected = false;
+ paintCell(p, r, c, QRect(colp, rowp, colw, rowh), selected);
+ p->translate(-colp, -rowp);
+
+ rowp = oldrp;
+ rowh = oldrh;
+
+ QWidget *w = cellWidget(r, c);
+ QRect cg(cellGeometry(r, c));
+ if (w && w->geometry() != QRect(contentsToViewport(cg.topLeft()), cg.size() - QSize(1, 1))) {
+ moveChild(w, colp, rowp);
+ w->resize(cg.size() - QSize(1, 1));
+ }
+ }
+ }
+ d->lastVisCol = collast;
+ d->lastVisRow = rowlast;
+
+ // draw indication of current cell
+ QRect focusRect = cellGeometry(curRow, curCol);
+ p->translate(focusRect.x(), focusRect.y());
+ paintFocus(p, focusRect);
+ p->translate(-focusRect.x(), -focusRect.y());
+
+ // Paint empty rects
+ paintEmptyArea(p, cx, cy, cw, ch);
+
+ drawActiveSelection = true;
+}
+
+/*!
+ \reimp
+
+ (Implemented to get rid of a compiler warning.)
+*/
+
+void Q3Table::drawContents(QPainter *)
+{
+}
+
+/*!
+ Returns the geometry of cell \a row, \a col in the cell's
+ coordinate system. This is a convenience function useful in
+ paintCell(). It is equivalent to QRect(QPoint(0,0), cellGeometry(
+ row, col).size());
+
+ \sa cellGeometry()
+*/
+
+QRect Q3Table::cellRect(int row, int col) const
+{
+ return QRect(QPoint(0,0), cellGeometry(row, col).size());
+}
+
+/*!
+ \overload
+
+ Use the other paintCell() function. This function is only included
+ for backwards compatibility.
+*/
+
+void Q3Table::paintCell(QPainter* p, int row, int col,
+ const QRect &cr, bool selected)
+{
+ if (cr.width() == 0 || cr.height() == 0)
+ return;
+#if defined(Q_WS_WIN)
+ const QColorGroup &cg = (!drawActiveSelection && style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus) ? palette().inactive() : colorGroup());
+#else
+ const QColorGroup &cg = colorGroup();
+#endif
+
+ Q3TableItem *itm = item(row, col);
+ QColorGroup cg2(cg);
+ if (itm && !itm->isEnabled())
+ cg2 = palette().disabled();
+
+ paintCell(p, row, col, cr, selected, cg2);
+}
+
+/*!
+ Paints the cell at \a row, \a col on the painter \a p. The painter
+ has already been translated to the cell's origin. \a cr describes
+ the cell coordinates in the content coordinate system.
+
+ If \a selected is true the cell is highlighted.
+
+ \a cg is the colorgroup which should be used to draw the cell
+ content.
+
+ If you want to draw custom cell content, for example right-aligned
+ text, you must either reimplement paintCell(), or subclass
+ Q3TableItem and reimplement Q3TableItem::paint() to do the custom
+ drawing.
+
+ If you're using a Q3TableItem subclass, for example, to store a
+ data structure, then reimplementing Q3TableItem::paint() may be the
+ best approach. For data you want to draw immediately, e.g. data
+ retrieved from a database, it is probably best to reimplement
+ paintCell(). Note that if you reimplement paintCell(), i.e. don't
+ use \l{Q3TableItem}s, you must reimplement other functions: see the
+ notes on large tables.
+
+ Note that the painter is not clipped by default in order to get
+ maximum efficiency. If you want clipping, use code like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 4
+*/
+
+void Q3Table::paintCell(QPainter *p, int row, int col,
+ const QRect &cr, bool selected, const QColorGroup &cg)
+{
+ if (focusStl == SpreadSheet && selected &&
+ row == curRow &&
+ col == curCol && (hasFocus() || viewport()->hasFocus()))
+ selected = false;
+
+ QPalette pal = cg;
+ int w = cr.width();
+ int h = cr.height();
+ int x2 = w - 1;
+ int y2 = h - 1;
+
+
+ Q3TableItem *itm = item(row, col);
+ if (itm) {
+ p->save();
+ itm->paint(p, pal, cr, selected);
+ p->restore();
+ } else {
+ p->fillRect(0, 0, w, h, selected ? pal.brush(QPalette::Highlight) : pal.brush(QPalette::Base));
+ }
+
+ if (sGrid) {
+ // Draw our lines
+ QPen pen(p->pen());
+ int gridColor = style()->styleHint(QStyle::SH_Table_GridLineColor, 0, this);
+ if (gridColor != -1) {
+ if (palette() != pal)
+ p->setPen(pal.mid().color());
+ else
+ p->setPen((QRgb)gridColor);
+ } else {
+ p->setPen(pal.mid().color());
+ }
+ p->drawLine(x2, 0, x2, y2);
+ p->drawLine(0, y2, x2, y2);
+ p->setPen(pen);
+ }
+}
+
+/*!
+ Draws the focus rectangle of the current cell (see currentRow(),
+ currentColumn()).
+
+ The painter \a p is already translated to the cell's origin, while
+ \a cr specifies the cell's geometry in content coordinates.
+*/
+
+void Q3Table::paintFocus(QPainter *p, const QRect &cr)
+{
+ if (!hasFocus() && !viewport()->hasFocus())
+ return;
+ QRect focusRect(0, 0, cr.width(), cr.height());
+ if (focusStyle() == SpreadSheet) {
+ p->setPen(QPen(Qt::black, 1));
+ p->setBrush(Qt::NoBrush);
+ p->drawRect(focusRect.x(), focusRect.y(), focusRect.width() - 1, focusRect.height() - 1);
+ p->drawRect(focusRect.x() - 1, focusRect.y() - 1, focusRect.width() + 1, focusRect.height() + 1);
+ } else {
+ QStyleOptionFocusRect opt;
+ opt.init(this);
+ opt.rect = focusRect;
+ opt.palette = palette();
+ opt.state |= QStyle::State_KeyboardFocusChange;
+ if (isSelected(curRow, curCol, false)) {
+ opt.state |= QStyle::State_FocusAtBorder;
+ opt.backgroundColor = palette().highlight().color();
+ } else {
+ opt.state |= QStyle::State_None;
+ opt.backgroundColor = palette().base().color();
+ }
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this);
+ }
+}
+
+/*!
+ This function fills the \a cw pixels wide and \a ch pixels high
+ rectangle starting at position \a cx, \a cy with the background
+ color using the painter \a p.
+
+ paintEmptyArea() is invoked by drawContents() to erase or fill
+ unused areas.
+*/
+
+void Q3Table::paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ // Regions work with shorts, so avoid an overflow and adjust the
+ // table size to the visible size
+ QSize ts(tableSize());
+ ts.setWidth(QMIN(ts.width(), visibleWidth()));
+ ts.setHeight(QMIN(ts.height(), visibleHeight()));
+
+ // Region of the rect we should draw, calculated in viewport
+ // coordinates, as a region can't handle bigger coordinates
+ contentsToViewport2(cx, cy, cx, cy);
+ QRegion reg(QRect(cx, cy, cw, ch));
+
+ // Subtract the table from it
+ reg = reg.subtracted(QRect(QPoint(0, 0), ts));
+
+ // And draw the rectangles (transformed inc contents coordinates as needed)
+ Q3MemArray<QRect> r = reg.rects();
+ for (int i = 0; i < (int)r.count(); ++i)
+ p->fillRect(QRect(viewportToContents2(r[i].topLeft()),r[i].size()), viewport()->backgroundBrush());
+}
+
+/*!
+ Returns the Q3TableItem representing the contents of the cell at \a
+ row, \a col.
+
+ If \a row or \a col are out of range or no content has been set
+ for this cell, item() returns 0.
+
+ If you don't use \l{Q3TableItem}s you may need to reimplement this
+ function: see the notes on large tables.
+
+ \sa setItem()
+*/
+
+Q3TableItem *Q3Table::item(int row, int col) const
+{
+ if (row < 0 || col < 0 || row > numRows() - 1 ||
+ col > numCols() - 1 || row * col >= (int)contents.size())
+ return 0;
+
+ return contents[ indexOf(row, col) ]; // contents array lookup
+}
+
+/*!
+ Inserts the table item \a item into the table at row \a row,
+ column \a col, and repaints the cell. If a table item already
+ exists in this cell it is deleted and replaced with \a item. The
+ table takes ownership of the table item.
+
+ If you don't use \l{Q3TableItem}s you may need to reimplement this
+ function: see the notes on large tables.
+
+ \sa item() takeItem()
+*/
+
+void Q3Table::setItem(int row, int col, Q3TableItem *item)
+{
+ if (!item)
+ return;
+
+ if ((int)contents.size() != numRows() * numCols())
+ resizeData(numRows() * numCols());
+
+ int orow = item->row();
+ int ocol = item->col();
+ clearCell(row, col);
+
+ contents.insert(indexOf(row, col), item);
+ item->setRow(row);
+ item->setCol(col);
+ item->t = this;
+ updateCell(row, col);
+ if (qt_update_cell_widget)
+ item->updateEditor(orow, ocol);
+
+ if (row == curRow && col == curCol && item->editType() == Q3TableItem::WhenCurrent) {
+ if (beginEdit(row, col, false))
+ setEditMode(Editing, row, col);
+ }
+}
+
+/*!
+ Removes the Q3TableItem at \a row, \a col.
+
+ If you don't use \l{Q3TableItem}s you may need to reimplement this
+ function: see the notes on large tables.
+*/
+
+void Q3Table::clearCell(int row, int col)
+{
+ if ((int)contents.size() != numRows() * numCols())
+ resizeData(numRows() * numCols());
+ clearCellWidget(row, col);
+ contents.setAutoDelete(true);
+ contents.remove(indexOf(row, col));
+ contents.setAutoDelete(false);
+}
+
+/*!
+ Sets the text in the cell at \a row, \a col to \a text.
+
+ If the cell does not contain a table item a Q3TableItem is created
+ with an \link Q3TableItem::EditType EditType\endlink of \c OnTyping,
+ otherwise the existing table item's text (if any) is replaced with
+ \a text.
+
+ \sa text() setPixmap() setItem() Q3TableItem::setText()
+*/
+
+void Q3Table::setText(int row, int col, const QString &text)
+{
+ Q3TableItem *itm = item(row, col);
+ if (itm) {
+ itm->setText(text);
+ itm->updateEditor(row, col);
+ updateCell(row, col);
+ } else {
+ Q3TableItem *i = new Q3TableItem(this, Q3TableItem::OnTyping,
+ text, QPixmap());
+ setItem(row, col, i);
+ }
+}
+
+/*!
+ Sets the pixmap in the cell at \a row, \a col to \a pix.
+
+ If the cell does not contain a table item a Q3TableItem is created
+ with an \link Q3TableItem::EditType EditType\endlink of \c OnTyping,
+ otherwise the existing table item's pixmap (if any) is replaced
+ with \a pix.
+
+ Note that \l{Q3ComboTableItem}s and \l{Q3CheckTableItem}s don't show
+ pixmaps.
+
+ \sa pixmap() setText() setItem() Q3TableItem::setPixmap()
+*/
+
+void Q3Table::setPixmap(int row, int col, const QPixmap &pix)
+{
+ Q3TableItem *itm = item(row, col);
+ if (itm) {
+ itm->setPixmap(pix);
+ updateCell(row, col);
+ } else {
+ Q3TableItem *i = new Q3TableItem(this, Q3TableItem::OnTyping,
+ QString(), pix);
+ setItem(row, col, i);
+ }
+}
+
+/*!
+ Returns the text in the cell at \a row, \a col, or an empty string
+ if the relevant item does not exist or has no text.
+
+ \sa setText() setPixmap()
+*/
+
+QString Q3Table::text(int row, int col) const
+{
+ Q3TableItem *itm = item(row, col);
+ if (itm)
+ return itm->text();
+ return QString();
+}
+
+/*!
+ Returns the pixmap set for the cell at \a row, \a col, or a
+ null-pixmap if the cell contains no pixmap.
+
+ \sa setPixmap()
+*/
+
+QPixmap Q3Table::pixmap(int row, int col) const
+{
+ Q3TableItem *itm = item(row, col);
+ if (itm)
+ return itm->pixmap();
+ return QPixmap();
+}
+
+/*!
+ Moves the focus to the cell at \a row, \a col.
+
+ \sa currentRow() currentColumn()
+*/
+
+void Q3Table::setCurrentCell(int row, int col)
+{
+ setCurrentCell(row, col, true, true);
+}
+
+// need to use a define, as leftMargin() is protected
+#define VERTICALMARGIN \
+(QApplication::reverseLayout() ? \
+ rightMargin() \
+ : \
+ leftMargin() \
+)
+
+/*!
+ \reimp
+*/
+QVariant Q3Table::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ if (query == Qt::ImMicroFocus)
+ return QRect(columnPos(curCol) + leftMargin() - contentsX(), rowPos(curRow) + topMargin() - contentsY(),
+ columnWidth(curCol), rowHeight(curRow));
+ return QWidget::inputMethodQuery(query);
+
+}
+
+/*! \internal */
+
+void Q3Table::setCurrentCell(int row, int col, bool updateSelections, bool ensureVisible)
+{
+ Q3TableItem *oldItem = item(curRow, curCol);
+
+ if (row > numRows() - 1)
+ row = numRows() - 1;
+ if (col > numCols() - 1)
+ col = numCols() - 1;
+
+ if (curRow == row && curCol == col)
+ return;
+
+
+ Q3TableItem *itm = oldItem;
+ if (itm && itm->editType() != Q3TableItem::Always && itm->editType() != Q3TableItem::Never)
+ endEdit(curRow, curCol, true, false);
+ int oldRow = curRow;
+ int oldCol = curCol;
+ curRow = row;
+ curCol = col;
+ repaintCell(oldRow, oldCol);
+ repaintCell(curRow, curCol);
+ if (ensureVisible)
+ ensureCellVisible(curRow, curCol);
+ emit currentChanged(row, col);
+
+ if (oldCol != curCol) {
+ if (!isColumnSelected(oldCol))
+ topHeader->setSectionState(oldCol, Q3TableHeader::Normal);
+ else if (isRowSelection(selectionMode()))
+ topHeader->setSectionState(oldCol, Q3TableHeader::Selected);
+ topHeader->setSectionState(curCol, isColumnSelected(curCol, true) ?
+ Q3TableHeader::Selected : Q3TableHeader::Bold);
+ }
+
+ if (oldRow != curRow) {
+ if (!isRowSelected(oldRow))
+ leftHeader->setSectionState(oldRow, Q3TableHeader::Normal);
+ leftHeader->setSectionState(curRow, isRowSelected(curRow, true) ?
+ Q3TableHeader::Selected : Q3TableHeader::Bold);
+ }
+
+ itm = item(curRow, curCol);
+
+
+ if (cellWidget(oldRow, oldCol) &&
+ cellWidget(oldRow, oldCol)->hasFocus())
+ viewport()->setFocus();
+
+ if (itm && itm->editType() == Q3TableItem::WhenCurrent) {
+ if (beginEdit(curRow, curCol, false))
+ setEditMode(Editing, row, col);
+ } else if (itm && itm->editType() == Q3TableItem::Always) {
+ if (cellWidget(itm->row(), itm->col()))
+ cellWidget(itm->row(), itm->col())->setFocus();
+ }
+
+ if (updateSelections && isRowSelection(selectionMode()) &&
+ !isSelected(curRow, curCol, false)) {
+ if (selectionMode() == Q3Table::SingleRow)
+ clearSelection();
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ currentSel->init(curRow, 0);
+ currentSel->expandTo(curRow, numCols() - 1);
+ repaintSelections(0, currentSel);
+ }
+}
+
+/*!
+ Scrolls the table until the cell at \a row, \a col becomes
+ visible.
+*/
+
+void Q3Table::ensureCellVisible(int row, int col)
+{
+ if (!updatesEnabled() || !viewport()->updatesEnabled())
+ return;
+ int cw = columnWidth(col);
+ int rh = rowHeight(row);
+ if (cw < visibleWidth())
+ ensureVisible(columnPos(col) + cw / 2, rowPos(row) + rh / 2, cw / 2, rh / 2);
+ else
+ ensureVisible(columnPos(col), rowPos(row) + rh / 2, 0, rh / 2);
+}
+
+/*!
+ Returns true if the cell at \a row, \a col is selected; otherwise
+ returns false.
+
+ \sa isRowSelected() isColumnSelected()
+*/
+
+bool Q3Table::isSelected(int row, int col) const
+{
+ return isSelected(row, col, true);
+}
+
+/*! \internal */
+
+bool Q3Table::isSelected(int row, int col, bool includeCurrent) const
+{
+ Q3PtrListIterator<Q3TableSelection> it(selections);
+ Q3TableSelection *s;
+ while ((s = it.current()) != 0) {
+ ++it;
+ if (s->isActive() &&
+ row >= s->topRow() &&
+ row <= s->bottomRow() &&
+ col >= s->leftCol() &&
+ col <= s->rightCol())
+ return true;
+ if (includeCurrent && row == currentRow() && col == currentColumn())
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns true if row \a row is selected; otherwise returns false.
+
+ If \a full is false (the default), 'row is selected' means that at
+ least one cell in the row is selected. If \a full is true, then 'row
+ is selected' means every cell in the row is selected.
+
+ \sa isColumnSelected() isSelected()
+*/
+
+bool Q3Table::isRowSelected(int row, bool full) const
+{
+ if (!full) {
+ Q3PtrListIterator<Q3TableSelection> it(selections);
+ Q3TableSelection *s;
+ while ((s = it.current()) != 0) {
+ ++it;
+ if (s->isActive() &&
+ row >= s->topRow() &&
+ row <= s->bottomRow())
+ return true;
+ if (row == currentRow())
+ return true;
+ }
+ } else {
+ Q3PtrListIterator<Q3TableSelection> it(selections);
+ Q3TableSelection *s;
+ while ((s = it.current()) != 0) {
+ ++it;
+ if (s->isActive() &&
+ row >= s->topRow() &&
+ row <= s->bottomRow() &&
+ s->leftCol() == 0 &&
+ s->rightCol() == numCols() - 1)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ Returns true if column \a col is selected; otherwise returns false.
+
+ If \a full is false (the default), 'column is selected' means that
+ at least one cell in the column is selected. If \a full is true,
+ then 'column is selected' means every cell in the column is
+ selected.
+
+ \sa isRowSelected() isSelected()
+*/
+
+bool Q3Table::isColumnSelected(int col, bool full) const
+{
+ if (!full) {
+ Q3PtrListIterator<Q3TableSelection> it(selections);
+ Q3TableSelection *s;
+ while ((s = it.current()) != 0) {
+ ++it;
+ if (s->isActive() &&
+ col >= s->leftCol() &&
+ col <= s->rightCol())
+ return true;
+ if (col == currentColumn())
+ return true;
+ }
+ } else {
+ Q3PtrListIterator<Q3TableSelection> it(selections);
+ Q3TableSelection *s;
+ while ((s = it.current()) != 0) {
+ ++it;
+ if (s->isActive() &&
+ col >= s->leftCol() &&
+ col <= s->rightCol() &&
+ s->topRow() == 0 &&
+ s->bottomRow() == numRows() - 1)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \property Q3Table::numSelections
+ \brief The number of selections.
+
+ \sa currentSelection()
+*/
+
+int Q3Table::numSelections() const
+{
+ return selections.count();
+}
+
+/*!
+ Returns selection number \a num, or an inactive Q3TableSelection if \a
+ num is out of range (see Q3TableSelection::isActive()).
+*/
+
+Q3TableSelection Q3Table::selection(int num) const
+{
+ if (num < 0 || num >= (int)selections.count())
+ return Q3TableSelection();
+
+ Q3TableSelection *s = ((Q3Table*)this)->selections.at(num);
+ return *s;
+}
+
+/*!
+ Adds a selection described by \a s to the table and returns its
+ number or -1 if the selection is invalid.
+
+ Remember to call Q3TableSelection::init() and
+ Q3TableSelection::expandTo() to make the selection valid (see also
+ Q3TableSelection::isActive(), or use the
+ Q3TableSelection(int,int,int,int) constructor).
+
+ \sa numSelections() removeSelection() clearSelection()
+*/
+
+int Q3Table::addSelection(const Q3TableSelection &s)
+{
+ if (!s.isActive())
+ return -1;
+
+ const int maxr = numRows()-1;
+ const int maxc = numCols()-1;
+ currentSel = new Q3TableSelection(QMIN(s.anchorRow(), maxr), QMIN(s.anchorCol(), maxc),
+ QMIN(s.bottomRow(), maxr), QMIN(s.rightCol(), maxc));
+
+ selections.append(currentSel);
+
+ repaintSelections(0, currentSel, true, true);
+
+ emit selectionChanged();
+
+ return selections.count() - 1;
+}
+
+/*!
+ If the table has a selection, \a s, this selection is removed from
+ the table.
+
+ \sa addSelection() numSelections()
+*/
+
+void Q3Table::removeSelection(const Q3TableSelection &s)
+{
+ selections.setAutoDelete(false);
+ for (Q3TableSelection *sel = selections.first(); sel; sel = selections.next()) {
+ if (s == *sel) {
+ selections.removeRef(sel);
+ repaintSelections(sel, 0, true, true);
+ if (sel == currentSel)
+ currentSel = 0;
+ delete sel;
+ }
+ }
+ selections.setAutoDelete(true);
+ emit selectionChanged();
+}
+
+/*!
+ \overload
+
+ Removes selection number \a num from the table.
+
+ \sa numSelections() addSelection() clearSelection()
+*/
+
+void Q3Table::removeSelection(int num)
+{
+ if (num < 0 || num >= (int)selections.count())
+ return;
+
+ Q3TableSelection *s = selections.at(num);
+ if (s == currentSel)
+ currentSel = 0;
+ selections.removeRef(s);
+ repaintContents(false);
+}
+
+/*!
+ Returns the number of the current selection or -1 if there is no
+ current selection.
+
+ \sa numSelections()
+*/
+
+int Q3Table::currentSelection() const
+{
+ if (!currentSel)
+ return -1;
+ return ((Q3Table*)this)->selections.findRef(currentSel);
+}
+
+/*! Selects the range starting at \a start_row and \a start_col and
+ ending at \a end_row and \a end_col.
+
+ \sa Q3TableSelection
+*/
+
+void Q3Table::selectCells(int start_row, int start_col, int end_row, int end_col)
+{
+ const int maxr = numRows()-1;
+ const int maxc = numCols()-1;
+
+ start_row = QMIN(maxr, QMAX(0, start_row));
+ start_col = QMIN(maxc, QMAX(0, start_col));
+ end_row = QMIN(maxr, end_row);
+ end_col = QMIN(maxc, end_col);
+ Q3TableSelection sel(start_row, start_col, end_row, end_col);
+ addSelection(sel);
+}
+
+/*! Selects the row \a row.
+
+ \sa Q3TableSelection
+*/
+
+void Q3Table::selectRow(int row)
+{
+ row = QMIN(numRows()-1, row);
+ if (row < 0)
+ return;
+ if (selectionMode() == SingleRow) {
+ setCurrentCell(row, currentColumn());
+ } else {
+ Q3TableSelection sel(row, 0, row, numCols() - 1);
+ addSelection(sel);
+ }
+}
+
+/*! Selects the column \a col.
+
+ \sa Q3TableSelection
+*/
+
+void Q3Table::selectColumn(int col)
+{
+ col = QMIN(numCols()-1, col);
+ if (col < 0)
+ return;
+ Q3TableSelection sel(0, col, numRows() - 1, col);
+ addSelection(sel);
+}
+
+/*! \reimp
+*/
+void Q3Table::contentsMousePressEvent(QMouseEvent* e)
+{
+ contentsMousePressEventEx(e);
+}
+
+void Q3Table::contentsMousePressEventEx(QMouseEvent* e)
+{
+ shouldClearSelection = false;
+ if (isEditing()) {
+ if (!cellGeometry(editRow, editCol).contains(e->pos())) {
+ endEdit(editRow, editCol, true, edMode != Editing);
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+
+ d->redirectMouseEvent = false;
+
+ int tmpRow = rowAt(e->pos().y());
+ int tmpCol = columnAt(e->pos().x());
+ pressedRow = tmpRow;
+ pressedCol = tmpCol;
+ fixRow(tmpRow, e->pos().y());
+ fixCol(tmpCol, e->pos().x());
+ startDragCol = -1;
+ startDragRow = -1;
+
+ if (isSelected(tmpRow, tmpCol)) {
+ startDragCol = tmpCol;
+ startDragRow = tmpRow;
+ dragStartPos = e->pos();
+ }
+
+ Q3TableItem *itm = item(pressedRow, pressedCol);
+ if (itm && !itm->isEnabled()) {
+ emit pressed(tmpRow, tmpCol, e->button(), e->pos());
+ return;
+ }
+
+ if ((e->state() & ShiftButton) == ShiftButton) {
+ int oldRow = curRow;
+ int oldCol = curCol;
+ setCurrentCell(tmpRow, tmpCol, selMode == SingleRow, true);
+ if (selMode != NoSelection && selMode != SingleRow) {
+ if (!currentSel) {
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ if (!isRowSelection(selectionMode()))
+ currentSel->init(oldRow, oldCol);
+ else
+ currentSel->init(oldRow, 0);
+ }
+ Q3TableSelection oldSelection = *currentSel;
+ if (!isRowSelection(selectionMode()))
+ currentSel->expandTo(tmpRow, tmpCol);
+ else
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ repaintSelections(&oldSelection, currentSel);
+ emit selectionChanged();
+ }
+ } else if ((e->state() & ControlButton) == ControlButton) {
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ if (selMode != NoSelection) {
+ if (selMode == Single || (selMode == SingleRow && !isSelected(tmpRow, tmpCol, false)))
+ clearSelection();
+ if (!(selMode == SingleRow && isSelected(tmpRow, tmpCol, false))) {
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ if (!isRowSelection(selectionMode())) {
+ currentSel->init(tmpRow, tmpCol);
+ currentSel->expandTo(tmpRow, tmpCol);
+ } else {
+ currentSel->init(tmpRow, 0);
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ repaintSelections(0, currentSel);
+ }
+ emit selectionChanged();
+ }
+ }
+ } else {
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ Q3TableItem *itm = item(tmpRow, tmpCol);
+ if (itm && itm->editType() == Q3TableItem::WhenCurrent) {
+ QWidget *w = cellWidget(tmpRow, tmpCol);
+ if (qobject_cast<Q3ComboBox*>(w) || qobject_cast<QAbstractButton*>(w)) {
+ QMouseEvent ev(e->type(), w->mapFromGlobal(e->globalPos()),
+ e->globalPos(), e->button(), e->state());
+ QApplication::sendPostedEvents(w, 0);
+ QApplication::sendEvent(w, &ev);
+ d->redirectMouseEvent = true;
+ }
+ }
+ if (isSelected(tmpRow, tmpCol, false)) {
+ shouldClearSelection = true;
+ } else {
+ bool b = signalsBlocked();
+ if (selMode != NoSelection)
+ blockSignals(true);
+ clearSelection();
+ blockSignals(b);
+ if (selMode != NoSelection) {
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ if (!isRowSelection(selectionMode())) {
+ currentSel->init(tmpRow, tmpCol);
+ currentSel->expandTo(tmpRow, tmpCol);
+ } else {
+ currentSel->init(tmpRow, 0);
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ repaintSelections(0, currentSel);
+ }
+ emit selectionChanged();
+ }
+ }
+ }
+
+ emit pressed(tmpRow, tmpCol, e->button(), e->pos());
+}
+
+/*! \reimp
+*/
+
+void Q3Table::contentsMouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (e->button() != LeftButton)
+ return;
+ if (!isRowSelection(selectionMode()))
+ clearSelection();
+ int tmpRow = rowAt(e->pos().y());
+ int tmpCol = columnAt(e->pos().x());
+ Q3TableItem *itm = item(tmpRow, tmpCol);
+ if (itm && !itm->isEnabled())
+ return;
+ if (tmpRow != -1 && tmpCol != -1) {
+ if (beginEdit(tmpRow, tmpCol, false))
+ setEditMode(Editing, tmpRow, tmpCol);
+ }
+
+ emit doubleClicked(tmpRow, tmpCol, e->button(), e->pos());
+}
+
+/*!
+ Sets the current edit mode to \a mode, the current edit row to \a
+ row and the current edit column to \a col.
+
+ \sa EditMode
+*/
+
+void Q3Table::setEditMode(EditMode mode, int row, int col)
+{
+ edMode = mode;
+ editRow = row;
+ editCol = col;
+}
+
+
+/*! \reimp
+*/
+
+void Q3Table::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ if ((e->state() & MouseButtonMask) == NoButton)
+ return;
+ int tmpRow = rowAt(e->pos().y());
+ int tmpCol = columnAt(e->pos().x());
+ fixRow(tmpRow, e->pos().y());
+ fixCol(tmpCol, e->pos().x());
+
+#ifndef QT_NO_DRAGANDDROP
+ if (dragEnabled() && startDragRow != -1 && startDragCol != -1) {
+ if (QPoint(dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance())
+ startDrag();
+ return;
+ }
+#endif
+ if (selectionMode() == MultiRow && (e->state() & ControlButton) == ControlButton)
+ shouldClearSelection = false;
+
+ if (shouldClearSelection) {
+ clearSelection();
+ if (selMode != NoSelection) {
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ if (!isRowSelection(selectionMode()))
+ currentSel->init(tmpRow, tmpCol);
+ else
+ currentSel->init(tmpRow, 0);
+ emit selectionChanged();
+ }
+ shouldClearSelection = false;
+ }
+
+ QPoint pos = mapFromGlobal(e->globalPos());
+ pos -= QPoint(leftHeader->width(), topHeader->height());
+ autoScrollTimer->stop();
+ doAutoScroll();
+ if (pos.x() < 0 || pos.x() > visibleWidth() || pos.y() < 0 || pos.y() > visibleHeight())
+ autoScrollTimer->start(100, true);
+}
+
+/*! \internal
+ */
+
+void Q3Table::doValueChanged()
+{
+ emit valueChanged(editRow, editCol);
+}
+
+/*! \internal
+*/
+
+void Q3Table::doAutoScroll()
+{
+ QPoint pos = QCursor::pos();
+ pos = mapFromGlobal(pos);
+ pos -= QPoint(leftHeader->width(), topHeader->height());
+
+ int tmpRow = curRow;
+ int tmpCol = curCol;
+ if (pos.y() < 0)
+ tmpRow--;
+ else if (pos.y() > visibleHeight())
+ tmpRow++;
+ if (pos.x() < 0)
+ tmpCol--;
+ else if (pos.x() > visibleWidth())
+ tmpCol++;
+
+ pos += QPoint(contentsX(), contentsY());
+ if (tmpRow == curRow)
+ tmpRow = rowAt(pos.y());
+ if (tmpCol == curCol)
+ tmpCol = columnAt(pos.x());
+ pos -= QPoint(contentsX(), contentsY());
+
+ fixRow(tmpRow, pos.y());
+ fixCol(tmpCol, pos.x());
+
+ if (tmpRow < 0 || tmpRow > numRows() - 1)
+ tmpRow = currentRow();
+ if (tmpCol < 0 || tmpCol > numCols() - 1)
+ tmpCol = currentColumn();
+
+ ensureCellVisible(tmpRow, tmpCol);
+
+ if (currentSel && selMode != NoSelection) {
+ Q3TableSelection oldSelection = *currentSel;
+ bool useOld = true;
+ if (selMode != SingleRow) {
+ if (!isRowSelection(selectionMode())) {
+ currentSel->expandTo(tmpRow, tmpCol);
+ } else {
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ }
+ } else {
+ bool currentInSelection = tmpRow == curRow && isSelected(tmpRow, tmpCol);
+ if (!currentInSelection) {
+ useOld = false;
+ clearSelection();
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ currentSel->init(tmpRow, 0);
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ repaintSelections(0, currentSel);
+ } else {
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ }
+ }
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ repaintSelections(useOld ? &oldSelection : 0, currentSel);
+ if (currentSel && oldSelection != *currentSel)
+ emit selectionChanged();
+ } else {
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ }
+
+ if (pos.x() < 0 || pos.x() > visibleWidth() || pos.y() < 0 || pos.y() > visibleHeight())
+ autoScrollTimer->start(100, true);
+}
+
+/*! \reimp
+*/
+
+void Q3Table::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ if (pressedRow == curRow && pressedCol == curCol)
+ emit clicked(curRow, curCol, e->button(), e->pos());
+
+ if (e->button() != LeftButton)
+ return;
+ if (shouldClearSelection) {
+ int tmpRow = rowAt(e->pos().y());
+ int tmpCol = columnAt(e->pos().x());
+ fixRow(tmpRow, e->pos().y());
+ fixCol(tmpCol, e->pos().x());
+ clearSelection();
+ if (selMode != NoSelection) {
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ if (!isRowSelection(selectionMode())) {
+ currentSel->init(tmpRow, tmpCol);
+ } else {
+ currentSel->init(tmpRow, 0);
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ repaintSelections(0, currentSel);
+ }
+ emit selectionChanged();
+ }
+ shouldClearSelection = false;
+ }
+ autoScrollTimer->stop();
+
+ if (d->redirectMouseEvent && pressedRow == curRow && pressedCol == curCol &&
+ item(pressedRow, pressedCol) && item(pressedRow, pressedCol)->editType() ==
+ Q3TableItem::WhenCurrent) {
+ QWidget *w = cellWidget(pressedRow, pressedCol);
+ if (w) {
+ QMouseEvent ev(e->type(), w->mapFromGlobal(e->globalPos()),
+ e->globalPos(), e->button(), e->state());
+ QApplication::sendPostedEvents(w, 0);
+ bool old = w->testAttribute(Qt::WA_NoMousePropagation);
+ w->setAttribute(Qt::WA_NoMousePropagation, true);
+ QApplication::sendEvent(w, &ev);
+ w->setAttribute(Qt::WA_NoMousePropagation, old);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3Table::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+ if (!receivers(SIGNAL(contextMenuRequested(int,int,QPoint)))) {
+ e->ignore();
+ return;
+ }
+ if (e->reason() == QContextMenuEvent::Keyboard) {
+ QRect r = cellGeometry(curRow, curCol);
+ emit contextMenuRequested(curRow, curCol, viewport()->mapToGlobal(contentsToViewport(r.center())));
+ } else {
+ int tmpRow = rowAt(e->pos().y());
+ int tmpCol = columnAt(e->pos().x());
+ emit contextMenuRequested(tmpRow, tmpCol, e->globalPos());
+ }
+}
+
+
+/*! \reimp
+*/
+
+bool Q3Table::eventFilter(QObject *o, QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::KeyPress: {
+ Q3TableItem *itm = item(curRow, curCol);
+ QWidget *editorWidget = cellWidget(editRow, editCol);
+
+ if (isEditing() && editorWidget && o == editorWidget) {
+ itm = item(editRow, editCol);
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if (ke->key() == Key_Escape) {
+ if (!itm || itm->editType() == Q3TableItem::OnTyping)
+ endEdit(editRow, editCol, false, edMode != Editing);
+ return true;
+ }
+
+ if ((ke->state() == NoButton || ke->state() == Keypad)
+ && (ke->key() == Key_Return || ke->key() == Key_Enter)) {
+ if (!itm || itm->editType() == Q3TableItem::OnTyping)
+ endEdit(editRow, editCol, true, edMode != Editing);
+ activateNextCell();
+ return true;
+ }
+
+ if (ke->key() == Key_Tab || ke->key() == Key_BackTab) {
+ if (ke->state() & Qt::ControlButton)
+ return false;
+ if (!itm || itm->editType() == Q3TableItem::OnTyping)
+ endEdit(editRow, editCol, true, edMode != Editing);
+ if ((ke->key() == Key_Tab) && !(ke->state() & ShiftButton)) {
+ if (currentColumn() >= numCols() - 1)
+ return true;
+ int cc = QMIN(numCols() - 1, currentColumn() + 1);
+ while (cc < numCols()) {
+ Q3TableItem *i = item(currentRow(), cc);
+ if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
+ break;
+ ++cc;
+ }
+ setCurrentCell(currentRow(), cc);
+ } else { // Key_BackTab
+ if (currentColumn() == 0)
+ return true;
+ int cc = QMAX(0, currentColumn() - 1);
+ while (cc >= 0) {
+ Q3TableItem *i = item(currentRow(), cc);
+ if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
+ break;
+ --cc;
+ }
+ setCurrentCell(currentRow(), cc);
+ }
+ itm = item(curRow, curCol);
+ if (beginEdit(curRow, curCol, false))
+ setEditMode(Editing, curRow, curCol);
+ return true;
+ }
+
+ if ((edMode == Replacing ||
+ (itm && itm->editType() == Q3TableItem::WhenCurrent)) &&
+ (ke->key() == Key_Up || ke->key() == Key_Prior ||
+ ke->key() == Key_Home || ke->key() == Key_Down ||
+ ke->key() == Key_Next || ke->key() == Key_End ||
+ ke->key() == Key_Left || ke->key() == Key_Right)) {
+ if (!itm || itm->editType() == Q3TableItem::OnTyping) {
+ endEdit(editRow, editCol, true, edMode != Editing);
+ }
+ keyPressEvent(ke);
+ return true;
+ }
+ } else {
+ QObjectList l = viewport()->queryList("QWidget");
+ if (l.contains(o)) {
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if ((ke->state() & ControlButton) == ControlButton ||
+ (ke->key() != Key_Left && ke->key() != Key_Right &&
+ ke->key() != Key_Up && ke->key() != Key_Down &&
+ ke->key() != Key_Prior && ke->key() != Key_Next &&
+ ke->key() != Key_Home && ke->key() != Key_End))
+ return false;
+ keyPressEvent((QKeyEvent*)e);
+ return true;
+ }
+ }
+
+ } break;
+ case QEvent::FocusOut: {
+ QWidget *editorWidget = cellWidget(editRow, editCol);
+ if (isEditing() && editorWidget && o == editorWidget && ((QFocusEvent*)e)->reason() != Qt::PopupFocusReason) {
+ // if the editor is the parent of the new focus widget, do nothing
+ QWidget *w = QApplication::focusWidget();
+ while (w) {
+ w = w->parentWidget();
+ if (w == editorWidget)
+ break;
+ }
+ if (w)
+ break;
+ // otherwise, end editing
+ Q3TableItem *itm = item(editRow, editCol);
+ if (!itm || itm->editType() == Q3TableItem::OnTyping) {
+ endEdit(editRow, editCol, true, edMode != Editing);
+ return true;
+ }
+ }
+ break;
+ }
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+ if (o == this || o == viewport()) {
+ QWheelEvent* we = (QWheelEvent*)e;
+ scrollBy(0, -we->delta());
+ we->accept();
+ return true;
+ }
+#endif
+ default:
+ break;
+ }
+
+ return Q3ScrollView::eventFilter(o, e);
+}
+
+void Q3Table::fixCell(int &row, int &col, int key)
+{
+ if (rowHeight(row) > 0 && columnWidth(col) > 0)
+ return;
+ if (rowHeight(row) <= 0) {
+ if (key == Key_Down ||
+ key == Key_Next ||
+ key == Key_End) {
+ while (row < numRows() && rowHeight(row) <= 0)
+ row++;
+ if (rowHeight(row) <= 0)
+ row = curRow;
+ } else if (key == Key_Up ||
+ key == Key_Prior ||
+ key == Key_Home)
+ while (row >= 0 && rowHeight(row) <= 0)
+ row--;
+ if (rowHeight(row) <= 0)
+ row = curRow;
+ } else if (columnWidth(col) <= 0) {
+ if (key == Key_Left) {
+ while (col >= 0 && columnWidth(col) <= 0)
+ col--;
+ if (columnWidth(col) <= 0)
+ col = curCol;
+ } else if (key == Key_Right) {
+ while (col < numCols() && columnWidth(col) <= 0)
+ col++;
+ if (columnWidth(col) <= 0)
+ col = curCol;
+ }
+ }
+}
+
+/*! \reimp
+*/
+
+void Q3Table::keyPressEvent(QKeyEvent* e)
+{
+ if (isEditing() && item(editRow, editCol) &&
+ item(editRow, editCol)->editType() == Q3TableItem::OnTyping)
+ return;
+
+ int tmpRow = curRow;
+ int tmpCol = curCol;
+ int oldRow = tmpRow;
+ int oldCol = tmpCol;
+
+ bool navigationKey = false;
+ int r;
+ switch (e->key()) {
+ case Key_Left:
+ tmpCol = QMAX(0, tmpCol - 1);
+ navigationKey = true;
+ break;
+ case Key_Right:
+ tmpCol = QMIN(numCols() - 1, tmpCol + 1);
+ navigationKey = true;
+ break;
+ case Key_Up:
+ tmpRow = QMAX(0, tmpRow - 1);
+ navigationKey = true;
+ break;
+ case Key_Down:
+ tmpRow = QMIN(numRows() - 1, tmpRow + 1);
+ navigationKey = true;
+ break;
+ case Key_Prior:
+ r = QMAX(0, rowAt(rowPos(tmpRow) - visibleHeight()));
+ if (r < tmpRow || tmpRow < 0)
+ tmpRow = r;
+ navigationKey = true;
+ break;
+ case Key_Next:
+ r = QMIN(numRows() - 1, rowAt(rowPos(tmpRow) + visibleHeight()));
+ if (r > tmpRow)
+ tmpRow = r;
+ else
+ tmpRow = numRows() - 1;
+ navigationKey = true;
+ break;
+ case Key_Home:
+ tmpRow = 0;
+ navigationKey = true;
+ break;
+ case Key_End:
+ tmpRow = numRows() - 1;
+ navigationKey = true;
+ break;
+ case Key_F2:
+ if (beginEdit(tmpRow, tmpCol, false))
+ setEditMode(Editing, tmpRow, tmpCol);
+ break;
+ case Key_Enter: case Key_Return:
+ activateNextCell();
+ return;
+ case Key_Tab: case Key_BackTab:
+ if ((e->key() == Key_Tab) && !(e->state() & ShiftButton)) {
+ if (currentColumn() >= numCols() - 1)
+ return;
+ int cc = QMIN(numCols() - 1, currentColumn() + 1);
+ while (cc < numCols()) {
+ Q3TableItem *i = item(currentRow(), cc);
+ if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
+ break;
+ ++cc;
+ }
+ setCurrentCell(currentRow(), cc);
+ } else { // Key_BackTab
+ if (currentColumn() == 0)
+ return;
+ int cc = QMAX(0, currentColumn() - 1);
+ while (cc >= 0) {
+ Q3TableItem *i = item(currentRow(), cc);
+ if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled()))
+ break;
+ --cc;
+ }
+ setCurrentCell(currentRow(), cc);
+ }
+ return;
+ case Key_Escape:
+ e->ignore();
+ return;
+ default: // ... or start in-place editing
+ if (e->text()[ 0 ].isPrint()) {
+ Q3TableItem *itm = item(tmpRow, tmpCol);
+ if (!itm || itm->editType() == Q3TableItem::OnTyping) {
+ QWidget *w = beginEdit(tmpRow, tmpCol,
+ itm ? itm->isReplaceable() : true);
+ if (w) {
+ setEditMode((!itm || (itm && itm->isReplaceable())
+ ? Replacing : Editing), tmpRow, tmpCol);
+ QApplication::sendEvent(w, e);
+ return;
+ }
+ }
+ }
+ e->ignore();
+ return;
+ }
+
+ if (navigationKey) {
+ fixCell(tmpRow, tmpCol, e->key());
+ if ((e->state() & ShiftButton) == ShiftButton &&
+ selMode != NoSelection && selMode != SingleRow) {
+ bool justCreated = false;
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ if (!currentSel) {
+ justCreated = true;
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ if (!isRowSelection(selectionMode()))
+ currentSel->init(oldRow, oldCol);
+ else
+ currentSel->init(oldRow < 0 ? 0 : oldRow, 0);
+ }
+ Q3TableSelection oldSelection = *currentSel;
+ if (!isRowSelection(selectionMode()))
+ currentSel->expandTo(tmpRow, tmpCol);
+ else
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ repaintSelections(justCreated ? 0 : &oldSelection, currentSel);
+ emit selectionChanged();
+ } else {
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ if (!isRowSelection(selectionMode())) {
+ clearSelection();
+ } else {
+ bool currentInSelection = tmpRow == oldRow && isSelected(tmpRow, tmpCol, false);
+ if (!currentInSelection) {
+ bool hasOldSel = false;
+ Q3TableSelection oldSelection;
+ if (selectionMode() == MultiRow) {
+ bool b = signalsBlocked();
+ blockSignals(true);
+ clearSelection();
+ blockSignals(b);
+ } else {
+ if (currentSel) {
+ oldSelection = *currentSel;
+ hasOldSel = true;
+ selections.removeRef(currentSel);
+ leftHeader->setSectionState(oldSelection.topRow(), Q3TableHeader::Normal);
+ }
+ }
+ currentSel = new Q3TableSelection();
+ selections.append(currentSel);
+ currentSel->init(tmpRow, 0);
+ currentSel->expandTo(tmpRow, numCols() - 1);
+ repaintSelections(hasOldSel ? &oldSelection : 0, currentSel, !hasOldSel);
+ emit selectionChanged();
+ }
+ }
+ }
+ } else {
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ }
+}
+
+/*! \reimp
+*/
+
+void Q3Table::focusInEvent(QFocusEvent*)
+{
+ d->inMenuMode = false;
+ QWidget *editorWidget = cellWidget(editRow, editCol);
+ updateCell(curRow, curCol);
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this))
+ repaintSelections();
+ if (isEditing() && editorWidget)
+ editorWidget->setFocus();
+
+}
+
+
+/*! \reimp
+*/
+
+void Q3Table::focusOutEvent(QFocusEvent *e)
+{
+ updateCell(curRow, curCol);
+ if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) {
+ d->inMenuMode =
+ e->reason() == Qt::PopupFocusReason ||
+ (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar"));
+ if (!d->inMenuMode)
+ repaintSelections();
+ }
+}
+
+/*! \reimp
+*/
+
+QSize Q3Table::sizeHint() const
+{
+ if (cachedSizeHint().isValid())
+ return cachedSizeHint();
+
+ constPolish();
+
+ QSize s = tableSize();
+ QSize sh;
+ if (s.width() < 500 && s.height() < 500) {
+ sh = QSize(tableSize().width() + VERTICALMARGIN + 5,
+ tableSize().height() + topMargin() + 5);
+ } else {
+ sh = Q3ScrollView::sizeHint();
+ if (!topHeader->isHidden())
+ sh.setHeight(sh.height() + topHeader->height());
+ if (!leftHeader->isHidden())
+ sh.setWidth(sh.width() + leftHeader->width());
+ }
+ setCachedSizeHint(sh);
+ return sh;
+}
+
+/*! \reimp
+*/
+
+void Q3Table::viewportResizeEvent(QResizeEvent *e)
+{
+ Q3ScrollView::viewportResizeEvent(e);
+ updateGeometries();
+}
+
+/*! \reimp
+*/
+
+void Q3Table::showEvent(QShowEvent *e)
+{
+ Q3ScrollView::showEvent(e);
+ QRect r(cellGeometry(numRows() - 1, numCols() - 1));
+ resizeContents(r.right() + 1, r.bottom() + 1);
+ updateGeometries();
+}
+
+/*! \reimp
+*/
+
+void Q3Table::paintEvent(QPaintEvent *e)
+{
+ QRect topLeftCorner = QStyle::visualRect(layoutDirection(), rect(), QRect(frameWidth(), frameWidth(), VERTICALMARGIN, topMargin()));
+ erase(topLeftCorner); // erase instead of widget on top
+ Q3ScrollView::paintEvent(e);
+
+#ifdef Q_OS_WINCE
+ QPainter p(this);
+ p.drawLine(topLeftCorner.bottomLeft(), topLeftCorner.bottomRight());
+ p.drawLine(topLeftCorner.bottomRight(), topLeftCorner.topRight());
+#endif
+}
+
+static bool inUpdateCell = false;
+
+/*!
+ Repaints the cell at \a row, \a col.
+*/
+
+void Q3Table::updateCell(int row, int col)
+{
+ if (inUpdateCell || row < 0 || col < 0)
+ return;
+ inUpdateCell = true;
+ QRect cg = cellGeometry(row, col);
+ QRect r(contentsToViewport(QPoint(cg.x() - 2, cg.y() - 2)),
+ QSize(cg.width() + 4, cg.height() + 4));
+ viewport()->update(r);
+ inUpdateCell = false;
+}
+
+void Q3Table::repaintCell(int row, int col)
+{
+ if (row == -1 || col == -1)
+ return;
+ QRect cg = cellGeometry(row, col);
+ QRect r(QPoint(cg.x() - 2, cg.y() - 2),
+ QSize(cg.width() + 4, cg.height() + 4));
+ repaintContents(r, false);
+}
+
+void Q3Table::contentsToViewport2(int x, int y, int& vx, int& vy)
+{
+ const QPoint v = contentsToViewport2(QPoint(x, y));
+ vx = v.x();
+ vy = v.y();
+}
+
+QPoint Q3Table::contentsToViewport2(const QPoint &p)
+{
+ return QPoint(p.x() - contentsX(),
+ p.y() - contentsY());
+}
+
+QPoint Q3Table::viewportToContents2(const QPoint& vp)
+{
+ return QPoint(vp.x() + contentsX(),
+ vp.y() + contentsY());
+}
+
+void Q3Table::viewportToContents2(int vx, int vy, int& x, int& y)
+{
+ const QPoint c = viewportToContents2(QPoint(vx, vy));
+ x = c.x();
+ y = c.y();
+}
+
+/*!
+ This function should be called whenever the column width of \a col
+ has been changed. It updates the geometry of any affected columns
+ and repaints the table to reflect the changes it has made.
+*/
+
+void Q3Table::columnWidthChanged(int col)
+{
+ int p = columnPos(col);
+ if (d->hasColSpan)
+ p = contentsX();
+ updateContents(p, contentsY(), contentsWidth(), visibleHeight());
+ QSize s(tableSize());
+ int w = contentsWidth();
+ resizeContents(s.width(), s.height());
+ if (contentsWidth() < w)
+ repaintContents(s.width(), contentsY(),
+ w - s.width() + 1, visibleHeight(), true);
+ else
+ repaintContents(w, contentsY(),
+ s.width() - w + 1, visibleHeight(), false);
+
+ // update widgets that are affected by this change
+ if (widgets.size()) {
+ int last = isHidden() ? numCols() - 1 : d->lastVisCol;
+ for (int c = col; c <= last; ++c)
+ updateColWidgets(c);
+ }
+ delayedUpdateGeometries();
+}
+
+/*!
+ This function should be called whenever the row height of \a row
+ has been changed. It updates the geometry of any affected rows and
+ repaints the table to reflect the changes it has made.
+*/
+
+void Q3Table::rowHeightChanged(int row)
+{
+ int p = rowPos(row);
+ if (d->hasRowSpan)
+ p = contentsY();
+ updateContents(contentsX(), p, visibleWidth(), contentsHeight());
+ QSize s(tableSize());
+ int h = contentsHeight();
+ resizeContents(s.width(), s.height());
+ if (contentsHeight() < h) {
+ repaintContents(contentsX(), contentsHeight(),
+ visibleWidth(), h - s.height() + 1, true);
+ } else {
+ repaintContents(contentsX(), h,
+ visibleWidth(), s.height() - h + 1, false);
+ }
+
+ // update widgets that are affected by this change
+ if (widgets.size()) {
+ d->lastVisRow = rowAt(contentsY() + visibleHeight() + (s.height() - h + 1));
+ int last = isHidden() ? numRows() - 1 : d->lastVisRow;
+ for (int r = row; r <= last; ++r)
+ updateRowWidgets(r);
+ }
+ delayedUpdateGeometries();
+}
+
+/*! \internal */
+
+void Q3Table::updateRowWidgets(int row)
+{
+ for (int i = 0; i < numCols(); ++i) {
+ QWidget *w = cellWidget(row, i);
+ if (!w)
+ continue;
+ moveChild(w, columnPos(i), rowPos(row));
+ w->resize(columnWidth(i) - 1, rowHeight(row) - 1);
+ }
+}
+
+/*! \internal */
+
+void Q3Table::updateColWidgets(int col)
+{
+ for (int i = 0; i < numRows(); ++i) {
+ QWidget *w = cellWidget(i, col);
+ if (!w)
+ continue;
+ moveChild(w, columnPos(col), rowPos(i));
+ w->resize(columnWidth(col) - 1, rowHeight(i) - 1);
+ }
+}
+
+/*!
+ This function is called when column order is to be changed, i.e.
+ when the user moved the column header \a section from \a fromIndex
+ to \a toIndex.
+
+ If you want to change the column order programmatically, call
+ swapRows() or swapColumns();
+
+ \sa Q3Header::indexChange() rowIndexChanged()
+*/
+
+void Q3Table::columnIndexChanged(int, int fromIndex, int toIndex)
+{
+ if (doSort && lastSortCol == fromIndex && topHeader)
+ topHeader->setSortIndicator(toIndex, topHeader->sortIndicatorOrder());
+ repaintContents(contentsX(), contentsY(),
+ visibleWidth(), visibleHeight(), false);
+}
+
+/*!
+ This function is called when the order of the rows is to be
+ changed, i.e. the user moved the row header section \a section
+ from \a fromIndex to \a toIndex.
+
+ If you want to change the order programmatically, call swapRows()
+ or swapColumns();
+
+ \sa Q3Header::indexChange() columnIndexChanged()
+*/
+
+void Q3Table::rowIndexChanged(int, int, int)
+{
+ repaintContents(contentsX(), contentsY(),
+ visibleWidth(), visibleHeight(), false);
+}
+
+/*!
+ This function is called when the column \a col has been clicked.
+ The default implementation sorts this column if sorting() is true.
+*/
+
+void Q3Table::columnClicked(int col)
+{
+ if (!sorting())
+ return;
+
+ if (col == lastSortCol) {
+ asc = !asc;
+ } else {
+ lastSortCol = col;
+ asc = true;
+ }
+ sortColumn(lastSortCol, asc);
+}
+
+/*!
+ \property Q3Table::sorting
+ \brief whether a click on the header of a column sorts that column
+
+ \sa sortColumn()
+*/
+
+void Q3Table::setSorting(bool b)
+{
+ doSort = b;
+ if (topHeader)
+ topHeader->setSortIndicator(b ? lastSortCol : -1);
+}
+
+bool Q3Table::sorting() const
+{
+ return doSort;
+}
+
+static bool inUpdateGeometries = false;
+
+void Q3Table::delayedUpdateGeometries()
+{
+ d->geomTimer->start(0, true);
+}
+
+void Q3Table::updateGeometriesSlot()
+{
+ updateGeometries();
+}
+
+/*!
+ This function updates the geometries of the left and top header.
+ You do not normally need to call this function.
+*/
+
+void Q3Table::updateGeometries()
+{
+ if (inUpdateGeometries)
+ return;
+ inUpdateGeometries = true;
+ QSize ts = tableSize();
+ if (topHeader->offset() &&
+ ts.width() < topHeader->offset() + topHeader->width())
+ horizontalScrollBar()->setValue(ts.width() - topHeader->width());
+ if (leftHeader->offset() &&
+ ts.height() < leftHeader->offset() + leftHeader->height())
+ verticalScrollBar()->setValue(ts.height() - leftHeader->height());
+
+ leftHeader->setGeometry(QStyle::visualRect(layoutDirection(), rect(), QRect(frameWidth(), topMargin() + frameWidth(),
+ VERTICALMARGIN, visibleHeight())));
+ topHeader->setGeometry(QStyle::visualRect(layoutDirection(), rect(), QRect(VERTICALMARGIN + frameWidth(), frameWidth(),
+ visibleWidth(), topMargin())));
+ horizontalScrollBar()->raise();
+ verticalScrollBar()->raise();
+ topHeader->updateStretches();
+ leftHeader->updateStretches();
+ inUpdateGeometries = false;
+}
+
+/*!
+ Returns the width of column \a col.
+
+ \sa setColumnWidth() rowHeight()
+*/
+
+int Q3Table::columnWidth(int col) const
+{
+ return topHeader->sectionSize(col);
+}
+
+/*!
+ Returns the height of row \a row.
+
+ \sa setRowHeight() columnWidth()
+*/
+
+int Q3Table::rowHeight(int row) const
+{
+ return leftHeader->sectionSize(row);
+}
+
+/*!
+ Returns the x-coordinate of the column \a col in content
+ coordinates.
+
+ \sa columnAt() rowPos()
+*/
+
+int Q3Table::columnPos(int col) const
+{
+ return topHeader->sectionPos(col);
+}
+
+/*!
+ Returns the y-coordinate of the row \a row in content coordinates.
+
+ \sa rowAt() columnPos()
+*/
+
+int Q3Table::rowPos(int row) const
+{
+ return leftHeader->sectionPos(row);
+}
+
+/*!
+ Returns the number of the column at position \a x. \a x must be
+ given in content coordinates.
+
+ \sa columnPos() rowAt()
+*/
+
+int Q3Table::columnAt(int x) const
+{
+ return topHeader->sectionAt(x);
+}
+
+/*!
+ Returns the number of the row at position \a y. \a y must be given
+ in content coordinates.
+
+ \sa rowPos() columnAt()
+*/
+
+int Q3Table::rowAt(int y) const
+{
+ return leftHeader->sectionAt(y);
+}
+
+/*!
+ Returns the bounding rectangle of the cell at \a row, \a col in
+ content coordinates.
+*/
+
+QRect Q3Table::cellGeometry(int row, int col) const
+{
+ Q3TableItem *itm = item(row, col);
+
+ if (!itm || (itm->rowSpan() == 1 && itm->colSpan() == 1))
+ return QRect(columnPos(col), rowPos(row),
+ columnWidth(col), rowHeight(row));
+
+ while (row != itm->row())
+ row--;
+ while (col != itm->col())
+ col--;
+
+ QRect rect(columnPos(col), rowPos(row),
+ columnWidth(col), rowHeight(row));
+
+ for (int r = 1; r < itm->rowSpan(); ++r)
+ rect.setHeight(rect.height() + rowHeight(r + row));
+
+ for (int c = 1; c < itm->colSpan(); ++c)
+ rect.setWidth(rect.width() + columnWidth(c + col));
+
+ return rect;
+}
+
+/*!
+ Returns the size of the table.
+
+ This is the same as the coordinates of the bottom-right edge of
+ the last table cell.
+*/
+
+QSize Q3Table::tableSize() const
+{
+ return QSize(columnPos(numCols() - 1) + columnWidth(numCols() - 1),
+ rowPos(numRows() - 1) + rowHeight(numRows() - 1));
+}
+
+/*!
+ \property Q3Table::numRows
+ \brief The number of rows in the table
+
+ \sa numCols
+*/
+
+int Q3Table::numRows() const
+{
+ return leftHeader->count();
+}
+
+/*!
+ \property Q3Table::numCols
+ \brief The number of columns in the table
+
+ \sa numRows
+*/
+
+int Q3Table::numCols() const
+{
+ return topHeader->count();
+}
+
+void Q3Table::saveContents(Q3PtrVector<Q3TableItem> &tmp,
+ Q3PtrVector<Q3Table::TableWidget> &tmp2)
+{
+ int nCols = numCols();
+ if (editRow != -1 && editCol != -1)
+ endEdit(editRow, editCol, false, edMode != Editing);
+ tmp.resize(contents.size());
+ tmp2.resize(widgets.size());
+ int i;
+ for (i = 0; i < (int)tmp.size(); ++i) {
+ Q3TableItem *item = contents[ i ];
+ if (item && (item->row() * nCols) + item->col() == i)
+ tmp.insert(i, item);
+ else
+ tmp.insert(i, 0);
+ }
+ for (i = 0; i < (int)tmp2.size(); ++i) {
+ QWidget *w = widgets[ i ];
+ if (w)
+ tmp2.insert(i, new TableWidget(w, i / nCols, i % nCols));
+ else
+ tmp2.insert(i, 0);
+ }
+}
+
+void Q3Table::updateHeaderAndResizeContents(Q3TableHeader *header,
+ int num, int rowCol,
+ int width, bool &updateBefore)
+{
+ updateBefore = rowCol < num;
+ if (rowCol > num) {
+ header->Q3Header::resizeArrays(rowCol);
+ header->Q3TableHeader::resizeArrays(rowCol);
+ int old = num;
+ clearSelection(false);
+ int i = 0;
+ for (i = old; i < rowCol; ++i)
+ header->addLabel(QString(), width);
+ } else {
+ clearSelection(false);
+ if (header == leftHeader) {
+ while (numRows() > rowCol)
+ header->removeLabel(numRows() - 1);
+ } else {
+ while (numCols() > rowCol)
+ header->removeLabel(numCols() - 1);
+ }
+ }
+
+ contents.setAutoDelete(false);
+ contents.clear();
+ contents.setAutoDelete(true);
+ widgets.setAutoDelete(false);
+ widgets.clear();
+ widgets.setAutoDelete(true);
+ resizeData(numRows() * numCols());
+
+ // keep numStretches in sync
+ int n = 0;
+ for (uint i = 0; i < header->stretchable.size(); i++)
+ n += (header->stretchable.at(i) & 1); // avoid cmp
+ header->numStretches = n;
+}
+
+void Q3Table::restoreContents(Q3PtrVector<Q3TableItem> &tmp,
+ Q3PtrVector<Q3Table::TableWidget> &tmp2)
+{
+ int i;
+ int nCols = numCols();
+ for (i = 0; i < (int)tmp.size(); ++i) {
+ Q3TableItem *it = tmp[ i ];
+ if (it) {
+ int idx = (it->row() * nCols) + it->col();
+ if ((uint)idx < contents.size() &&
+ it->row() == idx / nCols && it->col() == idx % nCols) {
+ contents.insert(idx, it);
+ if (it->rowSpan() > 1 || it->colSpan() > 1) {
+ int ridx, iidx;
+ for (int irow = 0; irow < it->rowSpan(); irow++) {
+ ridx = idx + irow * nCols;
+ for (int icol = 0; icol < it->colSpan(); icol++) {
+ iidx = ridx + icol;
+ if (idx != iidx && (uint)iidx < contents.size())
+ contents.insert(iidx, it);
+ }
+ }
+
+ }
+ } else {
+ delete it;
+ }
+ }
+ }
+ for (i = 0; i < (int)tmp2.size(); ++i) {
+ TableWidget *w = tmp2[ i ];
+ if (w) {
+ int idx = (w->row * nCols) + w->col;
+ if ((uint)idx < widgets.size() &&
+ w->row == idx / nCols && w->col == idx % nCols)
+ widgets.insert(idx, w->wid);
+ else
+ delete w->wid;
+ delete w;
+ }
+ }
+}
+
+void Q3Table::finishContentsResze(bool updateBefore)
+{
+ QRect r(cellGeometry(numRows() - 1, numCols() - 1));
+ resizeContents(r.right() + 1, r.bottom() + 1);
+ updateGeometries();
+ if (updateBefore)
+ repaintContents(contentsX(), contentsY(),
+ visibleWidth(), visibleHeight(), true);
+ else
+ repaintContents(contentsX(), contentsY(),
+ visibleWidth(), visibleHeight(), false);
+
+ if (isRowSelection(selectionMode())) {
+ int r = curRow;
+ curRow = -1;
+ setCurrentCell(r, curCol);
+ }
+}
+
+void Q3Table::setNumRows(int r)
+{
+ if (r < 0)
+ return;
+
+ if (r < numRows()) {
+ // Removed rows are no longer hidden, and should thus be removed from "hiddenRows"
+ for (int rr = numRows()-1; rr >= r; --rr) {
+ if (d->hiddenRows.find(rr))
+ d->hiddenRows.remove(rr);
+ }
+ }
+
+ fontChange(font()); // invalidate the sizeHintCache
+
+ Q3PtrVector<Q3TableItem> tmp;
+ Q3PtrVector<TableWidget> tmp2;
+ saveContents(tmp, tmp2);
+
+ bool updatesEnabled = leftHeader->updatesEnabled();
+ if (updatesEnabled)
+ leftHeader->setUpdatesEnabled(false);
+
+ bool updateBefore;
+ updateHeaderAndResizeContents(leftHeader, numRows(), r, 20, updateBefore);
+
+ int w = fontMetrics().width(QString::number(r) + QLatin1Char('W'));
+ if (VERTICALMARGIN > 0 && w > VERTICALMARGIN)
+ setLeftMargin(w);
+
+ restoreContents(tmp, tmp2);
+
+ leftHeader->calculatePositions();
+ finishContentsResze(updateBefore);
+ if (updatesEnabled) {
+ leftHeader->setUpdatesEnabled(true);
+ leftHeader->update();
+ }
+ leftHeader->updateCache();
+ if (curRow >= numRows()) {
+ curRow = numRows() - 1;
+ if (curRow < 0)
+ curCol = -1;
+ else
+ repaintCell(curRow, curCol);
+ }
+
+ if (curRow > numRows())
+ curRow = numRows();
+}
+
+void Q3Table::setNumCols(int c)
+{
+ if (c < 0)
+ return;
+
+ if (c < numCols()) {
+ // Removed columns are no longer hidden, and should thus be removed from "hiddenCols"
+ for (int cc = numCols()-1; cc >= c; --cc) {
+ if (d->hiddenCols.find(cc))
+ d->hiddenCols.remove(cc);
+ }
+ }
+
+ fontChange(font()); // invalidate the sizeHintCache
+
+ Q3PtrVector<Q3TableItem> tmp;
+ Q3PtrVector<TableWidget> tmp2;
+ saveContents(tmp, tmp2);
+
+ bool updatesEnabled = topHeader->updatesEnabled();
+ if (updatesEnabled)
+ topHeader->setUpdatesEnabled(false);
+
+ bool updateBefore;
+ updateHeaderAndResizeContents(topHeader, numCols(), c, 100, updateBefore);
+
+ restoreContents(tmp, tmp2);
+
+ topHeader->calculatePositions();
+ finishContentsResze(updateBefore);
+ if (updatesEnabled) {
+ topHeader->setUpdatesEnabled(true);
+ topHeader->update();
+ }
+ topHeader->updateCache();
+ if (curCol >= numCols()) {
+ curCol = numCols() - 1;
+ if (curCol < 0)
+ curRow = -1;
+ else
+ repaintCell(curRow, curCol);
+ }
+}
+
+/*! Sets the section labels of the verticalHeader() to \a labels */
+
+void Q3Table::setRowLabels(const QStringList &labels)
+{
+ leftHeader->setLabels(labels);
+}
+
+/*! Sets the section labels of the horizontalHeader() to \a labels */
+
+void Q3Table::setColumnLabels(const QStringList &labels)
+{
+ topHeader->setLabels(labels);
+}
+
+/*!
+ This function returns the widget which should be used as an editor
+ for the contents of the cell at \a row, \a col.
+
+ If \a initFromCell is true, the editor is used to edit the current
+ contents of the cell (so the editor widget should be initialized
+ with this content). If \a initFromCell is false, the content of
+ the cell is replaced with the new content which the user entered
+ into the widget created by this function.
+
+ The default functionality is as follows: if \a initFromCell is
+ true or the cell has a Q3TableItem and the table item's
+ Q3TableItem::isReplaceable() is false then the cell is asked to
+ create an appropriate editor (using Q3TableItem::createEditor()).
+ Otherwise a QLineEdit is used as the editor.
+
+ If you want to create your own editor for certain cells, implement
+ a custom Q3TableItem subclass and reimplement
+ Q3TableItem::createEditor().
+
+ If you are not using \l{Q3TableItem}s and you don't want to use a
+ QLineEdit as the default editor, subclass Q3Table and reimplement
+ this function with code like this:
+ \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 5
+ Ownership of the editor widget is transferred to the caller.
+
+ If you reimplement this function return 0 for read-only cells. You
+ will need to reimplement setCellContentFromEditor() to retrieve
+ the data the user entered.
+
+ \sa Q3TableItem::createEditor()
+*/
+
+QWidget *Q3Table::createEditor(int row, int col, bool initFromCell) const
+{
+ if (isReadOnly() || isRowReadOnly(row) || isColumnReadOnly(col))
+ return 0;
+
+ QWidget *e = 0;
+
+ // the current item in the cell should be edited if possible
+ Q3TableItem *i = item(row, col);
+ if (initFromCell || (i && !i->isReplaceable())) {
+ if (i) {
+ if (i->editType() == Q3TableItem::Never)
+ return 0;
+
+ e = i->createEditor();
+ if (!e)
+ return 0;
+ }
+ }
+
+ // no contents in the cell yet, so open the default editor
+ if (!e) {
+ e = new QLineEdit(viewport(), "qt_lineeditor");
+ ((QLineEdit*)e)->setFrame(false);
+ }
+
+ return e;
+}
+
+/*!
+ This function is called to start in-place editing of the cell at
+ \a row, \a col. Editing is achieved by creating an editor
+ (createEditor() is called) and setting the cell's editor with
+ setCellWidget() to the newly created editor. (After editing is
+ complete endEdit() will be called to replace the cell's content
+ with the editor's content.) If \a replace is true the editor will
+ start empty; otherwise it will be initialized with the cell's
+ content (if any), i.e. the user will be modifying the original
+ cell content.
+
+ \sa endEdit()
+*/
+
+QWidget *Q3Table::beginEdit(int row, int col, bool replace)
+{
+ if (isReadOnly() || isRowReadOnly(row) || isColumnReadOnly(col))
+ return 0;
+ if ( row < 0 || row >= numRows() || col < 0 || col >= numCols() )
+ return 0;
+ Q3TableItem *itm = item(row, col);
+ if (itm && !itm->isEnabled())
+ return 0;
+ if (cellWidget(row, col))
+ return 0;
+ ensureCellVisible(row, col);
+ QWidget *e = createEditor(row, col, !replace);
+ if (!e)
+ return 0;
+ setCellWidget(row, col, e);
+ e->setActiveWindow();
+ e->setFocus();
+ updateCell(row, col);
+ return e;
+}
+
+/*!
+ This function is called when in-place editing of the cell at \a
+ row, \a col is requested to stop.
+
+ If the cell is not being edited or \a accept is false the function
+ returns and the cell's contents are left unchanged.
+
+ If \a accept is true the content of the editor must be transferred
+ to the relevant cell. If \a replace is true the current content of
+ this cell should be replaced by the content of the editor (this
+ means removing the current Q3TableItem of the cell and creating a
+ new one for the cell). Otherwise (if possible) the content of the
+ editor should just be set to the existing Q3TableItem of this cell.
+
+ setCellContentFromEditor() is called to replace the contents of
+ the cell with the contents of the cell's editor.
+
+ Finally clearCellWidget() is called to remove the editor widget.
+
+ \sa setCellContentFromEditor(), beginEdit()
+*/
+
+void Q3Table::endEdit(int row, int col, bool accept, bool replace)
+{
+ QWidget *editor = cellWidget(row, col);
+ if (!editor)
+ return;
+
+ if (!accept) {
+ if (row == editRow && col == editCol)
+ setEditMode(NotEditing, -1, -1);
+ clearCellWidget(row, col);
+ updateCell(row, col);
+ viewport()->setFocus();
+ updateCell(row, col);
+ return;
+ }
+
+ Q3TableItem *i = item(row, col);
+ QString oldContent;
+ if (i)
+ oldContent = i->text();
+
+ if (!i || replace) {
+ setCellContentFromEditor(row, col);
+ i = item(row, col);
+ } else {
+ i->setContentFromEditor(editor);
+ }
+
+ if (row == editRow && col == editCol)
+ setEditMode(NotEditing, -1, -1);
+
+ viewport()->setFocus();
+ updateCell(row, col);
+
+ if (!i || (oldContent != i->text()))
+ emit valueChanged(row, col);
+
+ clearCellWidget(row, col);
+}
+
+/*!
+ This function is called to replace the contents of the cell at \a
+ row, \a col with the contents of the cell's editor.
+
+ If there already exists a Q3TableItem for the cell,
+ it calls Q3TableItem::setContentFromEditor() on this Q3TableItem.
+
+ If, for example, you want to create different \l{Q3TableItem}s
+ depending on the contents of the editor, you might reimplement
+ this function.
+
+ If you want to work without \l{Q3TableItem}s, you will need to
+ reimplement this function to save the data the user entered into
+ your data structure. (See the notes on large tables.)
+
+ \sa Q3TableItem::setContentFromEditor() createEditor()
+*/
+
+void Q3Table::setCellContentFromEditor(int row, int col)
+{
+ QWidget *editor = cellWidget(row, col);
+ if (!editor)
+ return;
+
+ Q3TableItem *i = item(row, col);
+ if (i) {
+ i->setContentFromEditor(editor);
+ } else {
+ QLineEdit *le = qobject_cast<QLineEdit*>(editor);
+ if (le)
+ setText(row, col, le->text());
+ }
+}
+
+/*!
+ Returns true if the \l EditMode is \c Editing or \c Replacing;
+ otherwise (i.e. the \l EditMode is \c NotEditing) returns false.
+
+ \sa Q3Table::EditMode
+*/
+
+bool Q3Table::isEditing() const
+{
+ return edMode != NotEditing;
+}
+
+/*!
+ Returns the current edit mode
+
+ \sa Q3Table::EditMode
+*/
+
+Q3Table::EditMode Q3Table::editMode() const
+{
+ return edMode;
+}
+
+/*!
+ Returns the current edited row
+*/
+
+int Q3Table::currEditRow() const
+{
+ return editRow;
+}
+
+/*!
+ Returns the current edited column
+*/
+
+int Q3Table::currEditCol() const
+{
+ return editCol;
+}
+
+/*!
+ Returns a single integer which identifies a particular \a row and \a
+ col by mapping the 2D table to a 1D array.
+
+ This is useful, for example, if you have a sparse table and want to
+ use a Q3IntDict to map integers to the cells that are used.
+*/
+
+int Q3Table::indexOf(int row, int col) const
+{
+ return (row * numCols()) + col;
+}
+
+/*! \internal
+*/
+
+void Q3Table::repaintSelections(Q3TableSelection *oldSelection,
+ Q3TableSelection *newSelection,
+ bool updateVertical, bool updateHorizontal)
+{
+ if (!oldSelection && !newSelection)
+ return;
+ if (oldSelection && newSelection && *oldSelection == *newSelection)
+ return;
+ if (oldSelection && !oldSelection->isActive())
+ oldSelection = 0;
+
+ bool optimizeOld = false;
+ bool optimizeNew = false;
+
+ QRect old;
+ if (oldSelection)
+ old = rangeGeometry(oldSelection->topRow(),
+ oldSelection->leftCol(),
+ oldSelection->bottomRow(),
+ oldSelection->rightCol(),
+ optimizeOld);
+ else
+ old = QRect(0, 0, 0, 0);
+
+ QRect cur;
+ if (newSelection)
+ cur = rangeGeometry(newSelection->topRow(),
+ newSelection->leftCol(),
+ newSelection->bottomRow(),
+ newSelection->rightCol(),
+ optimizeNew);
+ else
+ cur = QRect(0, 0, 0, 0);
+ int i;
+
+ if (!optimizeOld || !optimizeNew ||
+ old.width() > SHRT_MAX || old.height() > SHRT_MAX ||
+ cur.width() > SHRT_MAX || cur.height() > SHRT_MAX) {
+ QRect rr = cur.united(old);
+ repaintContents(rr, false);
+ } else {
+ old = QRect(contentsToViewport2(old.topLeft()), old.size());
+ cur = QRect(contentsToViewport2(cur.topLeft()), cur.size());
+ QRegion r1(old);
+ QRegion r2(cur);
+ QRegion r3 = r1.subtracted(r2);
+ QRegion r4 = r2.subtracted(r1);
+
+ for (i = 0; i < (int)r3.rects().count(); ++i) {
+ QRect r(r3.rects()[ i ]);
+ r = QRect(viewportToContents2(r.topLeft()), r.size());
+ repaintContents(r, false);
+ }
+ for (i = 0; i < (int)r4.rects().count(); ++i) {
+ QRect r(r4.rects()[ i ]);
+ r = QRect(viewportToContents2(r.topLeft()), r.size());
+ repaintContents(r, false);
+ }
+ }
+
+ int top, left, bottom, right;
+ {
+ int oldTopRow = oldSelection ? oldSelection->topRow() : numRows() - 1;
+ int newTopRow = newSelection ? newSelection->topRow() : numRows() - 1;
+ top = QMIN(oldTopRow, newTopRow);
+ }
+
+ {
+ int oldLeftCol = oldSelection ? oldSelection->leftCol() : numCols() - 1;
+ int newLeftCol = newSelection ? newSelection->leftCol() : numCols() - 1;
+ left = QMIN(oldLeftCol, newLeftCol);
+ }
+
+ {
+ int oldBottomRow = oldSelection ? oldSelection->bottomRow() : 0;
+ int newBottomRow = newSelection ? newSelection->bottomRow() : 0;
+ bottom = QMAX(oldBottomRow, newBottomRow);
+ }
+
+ {
+ int oldRightCol = oldSelection ? oldSelection->rightCol() : 0;
+ int newRightCol = newSelection ? newSelection->rightCol() : 0;
+ right = QMAX(oldRightCol, newRightCol);
+ }
+
+ if (updateHorizontal && numCols() > 0 && left >= 0 && !isRowSelection(selectionMode())) {
+ register int *s = &topHeader->states.data()[left];
+ for (i = left; i <= right; ++i) {
+ if (!isColumnSelected(i))
+ *s = Q3TableHeader::Normal;
+ else if (isColumnSelected(i, true))
+ *s = Q3TableHeader::Selected;
+ else
+ *s = Q3TableHeader::Bold;
+ ++s;
+ }
+ topHeader->repaint(false);
+ }
+
+ if (updateVertical && numRows() > 0 && top >= 0) {
+ register int *s = &leftHeader->states.data()[top];
+ for (i = top; i <= bottom; ++i) {
+ if (!isRowSelected(i))
+ *s = Q3TableHeader::Normal;
+ else if (isRowSelected(i, true))
+ *s = Q3TableHeader::Selected;
+ else
+ *s = Q3TableHeader::Bold;
+ ++s;
+ }
+ leftHeader->repaint(false);
+ }
+}
+
+/*!
+ Repaints all selections
+*/
+
+void Q3Table::repaintSelections()
+{
+ if (selections.isEmpty())
+ return;
+
+ QRect r;
+ for (Q3TableSelection *s = selections.first(); s; s = selections.next()) {
+ bool b;
+ r = r.united(rangeGeometry(s->topRow(),
+ s->leftCol(),
+ s->bottomRow(),
+ s->rightCol(), b));
+ }
+
+ repaintContents(r, false);
+}
+
+/*!
+ Clears all selections and repaints the appropriate regions if \a
+ repaint is true.
+
+ \sa removeSelection()
+*/
+
+void Q3Table::clearSelection(bool repaint)
+{
+ if (selections.isEmpty())
+ return;
+ bool needRepaint = !selections.isEmpty();
+
+ QRect r;
+ for (Q3TableSelection *s = selections.first(); s; s = selections.next()) {
+ bool b;
+ r = r.united(rangeGeometry(s->topRow(),
+ s->leftCol(),
+ s->bottomRow(),
+ s->rightCol(), b));
+ }
+
+ currentSel = 0;
+ selections.clear();
+
+ if (needRepaint && repaint)
+ repaintContents(r, false);
+
+ leftHeader->setSectionStateToAll(Q3TableHeader::Normal);
+ leftHeader->repaint(false);
+ if (!isRowSelection(selectionMode())) {
+ topHeader->setSectionStateToAll(Q3TableHeader::Normal);
+ topHeader->repaint(false);
+ }
+ topHeader->setSectionState(curCol, Q3TableHeader::Bold);
+ leftHeader->setSectionState(curRow, Q3TableHeader::Bold);
+ emit selectionChanged();
+}
+
+/*! \internal
+*/
+
+QRect Q3Table::rangeGeometry(int topRow, int leftCol,
+ int bottomRow, int rightCol, bool &optimize)
+{
+ topRow = QMAX(topRow, rowAt(contentsY()));
+ leftCol = QMAX(leftCol, columnAt(contentsX()));
+ int ra = rowAt(contentsY() + visibleHeight());
+ if (ra != -1)
+ bottomRow = QMIN(bottomRow, ra);
+ int ca = columnAt(contentsX() + visibleWidth());
+ if (ca != -1)
+ rightCol = QMIN(rightCol, ca);
+ optimize = true;
+ QRect rect;
+ for (int r = topRow; r <= bottomRow; ++r) {
+ for (int c = leftCol; c <= rightCol; ++c) {
+ rect = rect.united(cellGeometry(r, c));
+ Q3TableItem *i = item(r, c);
+ if (i && (i->rowSpan() > 1 || i->colSpan() > 1))
+ optimize = false;
+ }
+ }
+ return rect;
+}
+
+/*!
+ This function is called to activate the next cell if in-place
+ editing was finished by pressing the Enter key.
+
+ The default behaviour is to move from top to bottom, i.e. move to
+ the cell beneath the cell being edited. Reimplement this function
+ if you want different behaviour, e.g. moving from left to right.
+*/
+
+void Q3Table::activateNextCell()
+{
+ int firstRow = 0;
+ while (d->hiddenRows.find(firstRow))
+ firstRow++;
+ int firstCol = 0;
+ while (d->hiddenCols.find(firstCol))
+ firstCol++;
+ int nextRow = curRow;
+ int nextCol = curCol;
+ while (d->hiddenRows.find(++nextRow)) {}
+ if (nextRow >= numRows()) {
+ nextRow = firstRow;
+ while (d->hiddenCols.find(++nextCol)) {}
+ if (nextCol >= numCols())
+ nextCol = firstCol;
+ }
+
+ if (!currentSel || !currentSel->isActive() ||
+ (currentSel->leftCol() == currentSel->rightCol() &&
+ currentSel->topRow() == currentSel->bottomRow())) {
+ clearSelection();
+ setCurrentCell(nextRow, nextCol);
+ } else {
+ if (curRow < currentSel->bottomRow())
+ setCurrentCell(nextRow, curCol);
+ else if (curCol < currentSel->rightCol())
+ setCurrentCell(currentSel->topRow(), nextCol);
+ else
+ setCurrentCell(currentSel->topRow(), currentSel->leftCol());
+ }
+
+}
+
+/*! \internal
+*/
+
+void Q3Table::fixRow(int &row, int y)
+{
+ if (row == -1) {
+ if (y < 0)
+ row = 0;
+ else
+ row = numRows() - 1;
+ }
+}
+
+/*! \internal
+*/
+
+void Q3Table::fixCol(int &col, int x)
+{
+ if (col == -1) {
+ if (x < 0)
+ col = 0;
+ else
+ col = numCols() - 1;
+ }
+}
+
+struct SortableTableItem
+{
+ Q3TableItem *item;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int _cdecl cmpTableItems(const void *n1, const void *n2)
+#else
+static int cmpTableItems(const void *n1, const void *n2)
+#endif
+{
+ if (!n1 || !n2)
+ return 0;
+
+ SortableTableItem *i1 = (SortableTableItem *)n1;
+ SortableTableItem *i2 = (SortableTableItem *)n2;
+
+ return i1->item->key().localeAwareCompare(i2->item->key());
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+/*!
+ Sorts column \a col. If \a ascending is true the sort is in
+ ascending order, otherwise the sort is in descending order.
+
+ If \a wholeRows is true, entire rows are sorted using swapRows();
+ otherwise only cells in the column are sorted using swapCells().
+
+ Note that if you are not using Q3TableItems you will need to
+ reimplement swapRows() and swapCells(). (See the notes on large
+ tables.)
+
+ \sa swapRows()
+*/
+
+void Q3Table::sortColumn(int col, bool ascending, bool wholeRows)
+{
+ int filledRows = 0, i;
+ for (i = 0; i < numRows(); ++i) {
+ Q3TableItem *itm = item(i, col);
+ if (itm)
+ filledRows++;
+ }
+
+ if (!filledRows)
+ return;
+
+ SortableTableItem *items = new SortableTableItem[ filledRows ];
+ int j = 0;
+ for (i = 0; i < numRows(); ++i) {
+ Q3TableItem *itm = item(i, col);
+ if (!itm)
+ continue;
+ items[ j++ ].item = itm;
+ }
+
+ qsort(items, filledRows, sizeof(SortableTableItem), cmpTableItems);
+
+ bool updatesWereEnabled = updatesEnabled();
+ if (updatesWereEnabled)
+ setUpdatesEnabled(false);
+ for (i = 0; i < numRows(); ++i) {
+ if (i < filledRows) {
+ if (ascending) {
+ if (items[ i ].item->row() == i)
+ continue;
+ if (wholeRows)
+ swapRows(items[ i ].item->row(), i);
+ else
+ swapCells(items[ i ].item->row(), col, i, col);
+ } else {
+ if (items[ i ].item->row() == filledRows - i - 1)
+ continue;
+ if (wholeRows)
+ swapRows(items[ i ].item->row(), filledRows - i - 1);
+ else
+ swapCells(items[ i ].item->row(), col,
+ filledRows - i - 1, col);
+ }
+ }
+ }
+ if (updatesWereEnabled)
+ setUpdatesEnabled(true);
+ if (topHeader)
+ topHeader->setSortIndicator(col, ascending ? Qt::Ascending : Qt::Descending);
+
+ if (!wholeRows)
+ repaintContents(columnPos(col), contentsY(),
+ columnWidth(col), visibleHeight(), false);
+ else
+ repaintContents(contentsX(), contentsY(),
+ visibleWidth(), visibleHeight(), false);
+
+ delete [] items;
+}
+
+/*!
+ Hides row \a row.
+
+ \sa showRow() hideColumn()
+*/
+
+void Q3Table::hideRow(int row)
+{
+ if (d->hiddenRows.find(row))
+ return;
+ d->hiddenRows.replace(row, new int(leftHeader->sectionSize(row)));
+ leftHeader->resizeSection(row, 0);
+ leftHeader->setResizeEnabled(false, row);
+ if (isRowStretchable(row))
+ leftHeader->numStretches--;
+ rowHeightChanged(row);
+ if (curRow == row) {
+ int r = curRow;
+ int c = curCol;
+ int k = (r >= numRows() - 1 ? Key_Up : Key_Down);
+ fixCell(r, c, k);
+ if (numRows() > 0)
+ setCurrentCell(r, c);
+ }
+}
+
+/*!
+ Hides column \a col.
+
+ \sa showColumn() hideRow()
+*/
+
+void Q3Table::hideColumn(int col)
+{
+ if (!numCols() || d->hiddenCols.find(col))
+ return;
+ d->hiddenCols.replace(col, new int(topHeader->sectionSize(col)));
+ topHeader->resizeSection(col, 0);
+ topHeader->setResizeEnabled(false, col);
+ if (isColumnStretchable(col))
+ topHeader->numStretches--;
+ columnWidthChanged(col);
+ if (curCol == col) {
+ int r = curRow;
+ int c = curCol;
+ int k = (c >= numCols() - 1 ? Key_Left : Key_Right);
+ fixCell(r, c, k);
+ if (numCols() > 0)
+ setCurrentCell(r, c);
+ }
+}
+
+/*!
+ Shows row \a row.
+
+ \sa hideRow() showColumn()
+*/
+
+void Q3Table::showRow(int row)
+{
+ int *h = d->hiddenRows.find(row);
+ if (h) {
+ int rh = *h;
+ d->hiddenRows.remove(row);
+ setRowHeight(row, rh);
+ if (isRowStretchable(row))
+ leftHeader->numStretches++;
+ } else if (rowHeight(row) == 0) {
+ setRowHeight(row, 20);
+ }
+ leftHeader->setResizeEnabled(true, row);
+}
+
+/*!
+ Shows column \a col.
+
+ \sa hideColumn() showRow()
+*/
+
+void Q3Table::showColumn(int col)
+{
+ int *w = d->hiddenCols.find(col);
+ if (w) {
+ int cw = *w;
+ d->hiddenCols.remove(col);
+ setColumnWidth(col, cw);
+ if (isColumnStretchable(col))
+ topHeader->numStretches++;
+ } else if (columnWidth(col) == 0) {
+ setColumnWidth(col, 20);
+ }
+ topHeader->setResizeEnabled(true, col);
+}
+
+/*!
+ Returns true if row \a row is hidden; otherwise returns
+ false.
+
+ \sa hideRow(), isColumnHidden()
+*/
+bool Q3Table::isRowHidden(int row) const
+{
+ return d->hiddenRows.find(row);
+}
+
+/*!
+ Returns true if column \a col is hidden; otherwise returns
+ false.
+
+ \sa hideColumn(), isRowHidden()
+*/
+bool Q3Table::isColumnHidden(int col) const
+{
+ return d->hiddenCols.find(col);
+}
+
+/*!
+ Resizes column \a col to be \a w pixels wide.
+
+ \sa columnWidth() setRowHeight()
+*/
+
+void Q3Table::setColumnWidth(int col, int w)
+{
+ int *ow = d->hiddenCols.find(col);
+ if (ow) {
+ d->hiddenCols.replace(col, new int(w));
+ } else {
+ topHeader->resizeSection(col, w);
+ columnWidthChanged(col);
+ }
+}
+
+/*!
+ Resizes row \a row to be \a h pixels high.
+
+ \sa rowHeight() setColumnWidth()
+*/
+
+void Q3Table::setRowHeight(int row, int h)
+{
+ int *oh = d->hiddenRows.find(row);
+ if (oh) {
+ d->hiddenRows.replace(row, new int(h));
+ } else {
+ leftHeader->resizeSection(row, h);
+ rowHeightChanged(row);
+ }
+}
+
+/*!
+ Resizes column \a col so that the column width is wide enough to
+ display the widest item the column contains.
+
+ \sa adjustRow()
+*/
+
+void Q3Table::adjustColumn(int col)
+{
+ int w;
+ if ( currentColumn() == col ) {
+ QFont f = font();
+ f.setBold(true);
+ w = topHeader->sectionSizeHint( col, QFontMetrics(f) ).width();
+ } else {
+ w = topHeader->sectionSizeHint( col, fontMetrics() ).width();
+ }
+ if (topHeader->iconSet(col))
+ w += topHeader->iconSet(col)->pixmap().width();
+ w = QMAX(w, 20);
+ for (int i = 0; i < numRows(); ++i) {
+ Q3TableItem *itm = item(i, col);
+ if (!itm) {
+ QWidget *widget = cellWidget(i, col);
+ if (widget)
+ w = QMAX(w, widget->sizeHint().width());
+ } else {
+ if (itm->colSpan() > 1)
+ w = QMAX(w, itm->sizeHint().width() / itm->colSpan());
+ else
+ w = QMAX(w, itm->sizeHint().width());
+ }
+ }
+ w = QMAX(w, QApplication::globalStrut().width());
+ setColumnWidth(col, w);
+}
+
+/*!
+ Resizes row \a row so that the row height is tall enough to
+ display the tallest item the row contains.
+
+ \sa adjustColumn()
+*/
+
+void Q3Table::adjustRow(int row)
+{
+ int h = 20;
+ h = QMAX(h, leftHeader->sectionSizeHint(row, leftHeader->fontMetrics()).height());
+ if (leftHeader->iconSet(row))
+ h = QMAX(h, leftHeader->iconSet(row)->pixmap().height());
+ for (int i = 0; i < numCols(); ++i) {
+ Q3TableItem *itm = item(row, i);
+ if (!itm) {
+ QWidget *widget = cellWidget(row, i);
+ if (widget)
+ h = QMAX(h, widget->sizeHint().height());
+ } else {
+ if (itm->rowSpan() > 1)
+ h = QMAX(h, itm->sizeHint().height() / itm->rowSpan());
+ else
+ h = QMAX(h, itm->sizeHint().height());
+ }
+ }
+ h = QMAX(h, QApplication::globalStrut().height());
+ setRowHeight(row, h);
+}
+
+/*!
+ If \a stretch is true, column \a col is set to be stretchable;
+ otherwise column \a col is set to be unstretchable.
+
+ If the table widget's width decreases or increases stretchable
+ columns will grow narrower or wider to fit the space available as
+ completely as possible. The user cannot manually resize stretchable
+ columns.
+
+ \sa isColumnStretchable() setRowStretchable() adjustColumn()
+*/
+
+void Q3Table::setColumnStretchable(int col, bool stretch)
+{
+ topHeader->setSectionStretchable(col, stretch);
+
+ if (stretch && d->hiddenCols.find(col))
+ topHeader->numStretches--;
+}
+
+/*!
+ If \a stretch is true, row \a row is set to be stretchable;
+ otherwise row \a row is set to be unstretchable.
+
+ If the table widget's height decreases or increases stretchable
+ rows will grow shorter or taller to fit the space available as
+ completely as possible. The user cannot manually resize
+ stretchable rows.
+
+ \sa isRowStretchable() setColumnStretchable()
+*/
+
+void Q3Table::setRowStretchable(int row, bool stretch)
+{
+ leftHeader->setSectionStretchable(row, stretch);
+
+ if (stretch && d->hiddenRows.find(row))
+ leftHeader->numStretches--;
+}
+
+/*!
+ Returns true if column \a col is stretchable; otherwise returns
+ false.
+
+ \sa setColumnStretchable() isRowStretchable()
+*/
+
+bool Q3Table::isColumnStretchable(int col) const
+{
+ return topHeader->isSectionStretchable(col);
+}
+
+/*!
+ Returns true if row \a row is stretchable; otherwise returns
+ false.
+
+ \sa setRowStretchable() isColumnStretchable()
+*/
+
+bool Q3Table::isRowStretchable(int row) const
+{
+ return leftHeader->isSectionStretchable(row);
+}
+
+/*!
+ Takes the table item \a i out of the table. This function does \e
+ not delete the table item. You must either delete the table item
+ yourself or put it into a table (using setItem()) which will then
+ take ownership of it.
+
+ Use this function if you want to move an item from one cell in a
+ table to another, or to move an item from one table to another,
+ reinserting the item with setItem().
+
+ If you want to exchange two cells use swapCells().
+*/
+
+void Q3Table::takeItem(Q3TableItem *i)
+{
+ if (!i)
+ return;
+ if (i->row() != -1 && i->col() != -1) {
+ QRect rect = cellGeometry(i->row(), i->col());
+ contents.setAutoDelete(false);
+ int bottom = i->row() + i->rowSpan();
+ if (bottom > numRows())
+ bottom = numRows();
+ int right = i->col() + i->colSpan();
+ if (right > numCols())
+ right = numCols();
+ for (int r = i->row(); r < bottom; ++r) {
+ for (int c = i->col(); c < right; ++c)
+ contents.remove(indexOf(r, c));
+ }
+ contents.setAutoDelete(true);
+ repaintContents(rect, false);
+ int orow = i->row();
+ int ocol = i->col();
+ i->setRow(-1);
+ i->setCol(-1);
+ i->updateEditor(orow, ocol);
+ }
+ i->t = 0;
+}
+
+/*!
+ Sets the widget \a e to the cell at \a row, \a col and takes care of
+ placing and resizing the widget when the cell geometry changes.
+
+ By default widgets are inserted into a vector with numRows() *
+ numCols() elements. In very large tables you will probably want to
+ store the widgets in a data structure that consumes less memory (see
+ the notes on large tables). To support the use of your own data
+ structure this function calls insertWidget() to add the widget to
+ the internal data structure. To use your own data structure
+ reimplement insertWidget(), cellWidget() and clearCellWidget().
+
+ Cell widgets are created dynamically with the \c new operator. The
+ cell widgets are destroyed automatically once the table is
+ destroyed; the table takes ownership of the widget when using
+ setCellWidget.
+
+*/
+
+void Q3Table::setCellWidget(int row, int col, QWidget *e)
+{
+ if (!e || row >= numRows() || col >= numCols())
+ return;
+
+ QWidget *w = cellWidget(row, col);
+ if (w && row == editRow && col == editCol)
+ endEdit(editRow, editCol, false, edMode != Editing);
+
+ e->installEventFilter(this);
+ clearCellWidget(row, col);
+ if (e->parent() != viewport())
+ e->reparent(viewport(), QPoint(0,0));
+ Q3TableItem *itm = item(row, col);
+ if (itm && itm->row() >= 0 && itm->col() >= 0) { // get the correct row and col if the item is spanning
+ row = itm->row();
+ col = itm->col();
+ }
+ insertWidget(row, col, e);
+ QRect cr = cellGeometry(row, col);
+ e->resize(cr.size());
+ moveChild(e, cr.x(), cr.y());
+ e->show();
+}
+
+/*!
+ Inserts widget \a w at \a row, \a col into the internal
+ data structure. See the documentation of setCellWidget() for
+ further details.
+
+ If you don't use \l{Q3TableItem}s you may need to reimplement this
+ function: see the notes on large tables.
+*/
+
+void Q3Table::insertWidget(int row, int col, QWidget *w)
+{
+ if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
+ return;
+
+ if ((int)widgets.size() != numRows() * numCols())
+ widgets.resize(numRows() * numCols());
+
+ widgets.insert(indexOf(row, col), w);
+}
+
+/*!
+ Returns the widget that has been set for the cell at \a row, \a
+ col, or 0 if no widget has been set.
+
+ If you don't use \l{Q3TableItem}s you may need to reimplement this
+ function: see the notes on large tables.
+
+ \sa clearCellWidget() setCellWidget()
+*/
+
+QWidget *Q3Table::cellWidget(int row, int col) const
+{
+ if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
+ return 0;
+
+ if ((int)widgets.size() != numRows() * numCols())
+ ((Q3Table*)this)->widgets.resize(numRows() * numCols());
+
+ return widgets[ indexOf(row, col) ];
+}
+
+/*!
+ Removes the widget (if there is one) set for the cell at \a row,
+ \a col.
+
+ If you don't use \l{Q3TableItem}s you may need to reimplement this
+ function: see the notes on large tables.
+
+ This function deletes the widget at \a row, \a col. Note that the
+ widget is not deleted immediately; instead QObject::deleteLater()
+ is called on the widget to avoid problems with timing issues.
+
+ \sa cellWidget() setCellWidget()
+*/
+
+void Q3Table::clearCellWidget(int row, int col)
+{
+ if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
+ return;
+
+ if ((int)widgets.size() != numRows() * numCols())
+ widgets.resize(numRows() * numCols());
+
+ QWidget *w = cellWidget(row, col);
+ if (w) {
+ w->removeEventFilter(this);
+ w->hide();
+ w->deleteLater();
+ }
+ widgets.setAutoDelete(false);
+ widgets.remove(indexOf(row, col));
+ widgets.setAutoDelete(true);
+}
+
+/*!
+ \fn void Q3Table::dropped (QDropEvent * e)
+
+ This signal is emitted when a drop event occurred on the table.
+
+ \a e contains information about the drop.
+*/
+
+/*!
+ If \a b is true, the table starts a drag (see dragObject()) when
+ the user presses and moves the mouse on a selected cell.
+*/
+
+void Q3Table::setDragEnabled(bool b)
+{
+ dEnabled = b;
+}
+
+/*!
+ If this function returns true, the table supports dragging.
+
+ \sa setDragEnabled()
+*/
+
+bool Q3Table::dragEnabled() const
+{
+ return dEnabled;
+}
+
+/*!
+ Inserts \a count empty rows at row \a row. Also clears the selection(s).
+
+ \sa insertColumns() removeRow()
+*/
+
+void Q3Table::insertRows(int row, int count)
+{
+ // special case, so a call like insertRow(currentRow(), 1) also
+ // works, when we have 0 rows and currentRow() is -1
+ if (row == -1 && curRow == -1)
+ row = 0;
+ if (row < 0 || count <= 0)
+ return;
+
+ if (curRow >= row && curRow < row + count)
+ curRow = row + count;
+
+ --row;
+ if (row >= numRows())
+ return;
+
+ bool updatesWereEnabled = updatesEnabled();
+ if (updatesWereEnabled)
+ setUpdatesEnabled(false);
+ bool leftHeaderUpdatesEnabled = leftHeader->updatesEnabled();
+ if (leftHeaderUpdatesEnabled)
+ leftHeader->setUpdatesEnabled(false);
+ int oldLeftMargin = leftMargin();
+
+ setNumRows(numRows() + count);
+
+ for (int i = numRows() - count - 1; i > row; --i)
+ leftHeader->swapSections(i, i + count);
+
+ if (leftHeaderUpdatesEnabled)
+ leftHeader->setUpdatesEnabled(leftHeaderUpdatesEnabled);
+
+ if (updatesWereEnabled)
+ setUpdatesEnabled(true);
+
+ int cr = QMAX(0, currentRow());
+ int cc = QMAX(0, currentColumn());
+ if (curRow > row)
+ curRow -= count; // this is where curRow was
+ setCurrentCell(cr, cc, true, false); // without ensureCellVisible
+
+ // Repaint the header
+ if (leftHeaderUpdatesEnabled) {
+ int y = rowPos(row) - contentsY();
+ if (leftMargin() != oldLeftMargin || d->hasRowSpan)
+ y = 0; // full repaint
+ QRect rect(0, y, leftHeader->width(), contentsHeight());
+ leftHeader->update(rect);
+ }
+
+ if (updatesWereEnabled) {
+ int p = rowPos(row);
+ if (d->hasRowSpan)
+ p = contentsY();
+ updateContents(contentsX(), p, visibleWidth(), contentsHeight() + 1);
+ }
+}
+
+/*!
+ Inserts \a count empty columns at column \a col. Also clears the selection(s).
+
+ \sa insertRows() removeColumn()
+*/
+
+void Q3Table::insertColumns(int col, int count)
+{
+ // see comment in insertRows()
+ if (col == -1 && curCol == -1)
+ col = 0;
+ if (col < 0 || count <= 0)
+ return;
+
+ if (curCol >= col && curCol < col + count)
+ curCol = col + count;
+
+ --col;
+ if (col >= numCols())
+ return;
+
+ bool updatesWereEnabled = updatesEnabled();
+ if (updatesWereEnabled)
+ setUpdatesEnabled(false);
+ bool topHeaderUpdatesEnabled = topHeader->updatesEnabled();
+ if (topHeaderUpdatesEnabled)
+ topHeader->setUpdatesEnabled(false);
+ int oldTopMargin = topMargin();
+
+ setNumCols(numCols() + count);
+
+ for (int i = numCols() - count - 1; i > col; --i)
+ topHeader->swapSections(i, i + count);
+
+ if (topHeaderUpdatesEnabled)
+ topHeader->setUpdatesEnabled(true);
+ if (updatesWereEnabled)
+ setUpdatesEnabled(true);
+
+ int cr = QMAX(0, currentRow());
+ int cc = QMAX(0, currentColumn());
+ if (curCol > col)
+ curCol -= count; // this is where curCol was
+ setCurrentCell(cr, cc, true, false); // without ensureCellVisible
+
+ // Repaint the header
+ if (topHeaderUpdatesEnabled) {
+ int x = columnPos(col) - contentsX();
+ if (topMargin() != oldTopMargin || d->hasColSpan)
+ x = 0; // full repaint
+ QRect rect(x, 0, contentsWidth(), topHeader->height());
+ topHeader->update(rect);
+ }
+
+ if (updatesWereEnabled) {
+ int p = columnPos(col);
+ if (d->hasColSpan)
+ p = contentsX();
+ updateContents(p, contentsY(), contentsWidth() + 1, visibleHeight());
+ }
+}
+
+/*!
+ Removes row \a row, and deletes all its cells including any table
+ items and widgets the cells may contain. Also clears the selection(s).
+
+ \sa hideRow() insertRows() removeColumn() removeRows()
+*/
+
+void Q3Table::removeRow(int row)
+{
+ if (row < 0 || row >= numRows())
+ return;
+ if (row < numRows() - 1) {
+ if (d->hiddenRows.find(row))
+ d->hiddenRows.remove(row);
+
+ for (int i = row; i < numRows() - 1; ++i)
+ ((Q3TableHeader*)verticalHeader())->swapSections(i, i + 1);
+ }
+ setNumRows(numRows() - 1);
+}
+
+/*!
+ Removes the rows listed in the array \a rows, and deletes all their
+ cells including any table items and widgets the cells may contain.
+
+ The array passed in must only contain valid rows (in the range
+ from 0 to numRows() - 1) with no duplicates, and must be sorted in
+ ascending order. Also clears the selection(s).
+
+ \sa removeRow() insertRows() removeColumns()
+*/
+
+void Q3Table::removeRows(const Q3MemArray<int> &rows)
+{
+ if (rows.count() == 0)
+ return;
+ int i;
+ for (i = 0; i < (int)rows.count() - 1; ++i) {
+ for (int j = rows[i] - i; j < rows[i + 1] - i - 1; j++) {
+ ((Q3TableHeader*)verticalHeader())->swapSections(j, j + i + 1);
+ }
+ }
+
+ for (int j = rows[i] - i; j < numRows() - (int)rows.size(); j++)
+ ((Q3TableHeader*)verticalHeader())->swapSections(j, j + rows.count());
+
+ setNumRows(numRows() - rows.count());
+}
+
+/*!
+ Removes column \a col, and deletes all its cells including any
+ table items and widgets the cells may contain. Also clears the
+ selection(s).
+
+ \sa removeColumns() hideColumn() insertColumns() removeRow()
+*/
+
+void Q3Table::removeColumn(int col)
+{
+ if (col < 0 || col >= numCols())
+ return;
+ if (col < numCols() - 1) {
+ if (d->hiddenCols.find(col))
+ d->hiddenCols.remove(col);
+
+ for (int i = col; i < numCols() - 1; ++i)
+ ((Q3TableHeader*)horizontalHeader())->swapSections(i, i + 1);
+ }
+ setNumCols(numCols() - 1);
+}
+
+/*!
+ Removes the columns listed in the array \a cols, and deletes all
+ their cells including any table items and widgets the cells may
+ contain.
+
+ The array passed in must only contain valid columns (in the range
+ from 0 to numCols() - 1) with no duplicates, and must be sorted in
+ ascending order. Also clears the selection(s).
+
+ \sa removeColumn() insertColumns() removeRows()
+*/
+
+void Q3Table::removeColumns(const Q3MemArray<int> &cols)
+{
+ if (cols.count() == 0)
+ return;
+ int i;
+ for (i = 0; i < (int)cols.count() - 1; ++i) {
+ for (int j = cols[i] - i; j < cols[i + 1] - i - 1; j++) {
+ ((Q3TableHeader*)horizontalHeader())->swapSections(j, j + i + 1);
+ }
+ }
+
+ for (int j = cols[i] - i; j < numCols() - (int)cols.size(); j++)
+ ((Q3TableHeader*)horizontalHeader())->swapSections(j, j + cols.count());
+
+ setNumCols(numCols() - cols.count());
+}
+
+/*!
+ Starts editing the cell at \a row, \a col.
+
+ If \a replace is true the content of this cell will be replaced by
+ the content of the editor when editing is finished, i.e. the user
+ will be entering new data; otherwise the current content of the
+ cell (if any) will be modified in the editor.
+
+ \sa beginEdit()
+*/
+
+void Q3Table::editCell(int row, int col, bool replace)
+{
+ if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1)
+ return;
+
+ if (beginEdit(row, col, replace)) {
+ edMode = Editing;
+ editRow = row;
+ editCol = col;
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ This event handler is called whenever a Q3Table object receives a
+ \l QDragEnterEvent \a e, i.e. when the user pressed the mouse
+ button to drag something.
+
+ The focus is moved to the cell where the QDragEnterEvent occurred.
+*/
+
+void Q3Table::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ oldCurrentRow = curRow;
+ oldCurrentCol = curCol;
+ int tmpRow = rowAt(e->pos().y());
+ int tmpCol = columnAt(e->pos().x());
+ fixRow(tmpRow, e->pos().y());
+ fixCol(tmpCol, e->pos().x());
+ if (e->source() != (QObject*)cellWidget(currentRow(), currentColumn()))
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ e->accept();
+}
+
+/*!
+ This event handler is called whenever a Q3Table object receives a
+ \l QDragMoveEvent \a e, i.e. when the user actually drags the
+ mouse.
+
+ The focus is moved to the cell where the QDragMoveEvent occurred.
+*/
+
+void Q3Table::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ int tmpRow = rowAt(e->pos().y());
+ int tmpCol = columnAt(e->pos().x());
+ fixRow(tmpRow, e->pos().y());
+ fixCol(tmpCol, e->pos().x());
+ if (e->source() != (QObject*)cellWidget(currentRow(), currentColumn()))
+ setCurrentCell(tmpRow, tmpCol, false, true);
+ e->accept();
+}
+
+/*!
+ This event handler is called when a drag activity leaves \e this
+ Q3Table object with event \a e.
+*/
+
+void Q3Table::contentsDragLeaveEvent(QDragLeaveEvent *)
+{
+ setCurrentCell(oldCurrentRow, oldCurrentCol, false, true);
+}
+
+/*!
+ This event handler is called when the user ends a drag and drop by
+ dropping something onto \e this Q3Table and thus triggers the drop
+ event, \a e.
+*/
+
+void Q3Table::contentsDropEvent(QDropEvent *e)
+{
+ setCurrentCell(oldCurrentRow, oldCurrentCol, false, true);
+ emit dropped(e);
+}
+
+/*!
+ If the user presses the mouse on a selected cell, starts moving
+ (i.e. dragging), and dragEnabled() is true, this function is
+ called to obtain a drag object. A drag using this object begins
+ immediately unless dragObject() returns 0.
+
+ By default this function returns 0. You might reimplement it and
+ create a Q3DragObject depending on the selected items.
+
+ \sa dropped()
+*/
+
+Q3DragObject *Q3Table::dragObject()
+{
+ return 0;
+}
+
+/*!
+ Starts a drag.
+
+ Usually you don't need to call or reimplement this function yourself.
+
+ \sa dragObject()
+*/
+
+void Q3Table::startDrag()
+{
+ if (startDragRow == -1 || startDragCol == -1)
+ return;
+
+ startDragRow = startDragCol = -1;
+
+ Q3DragObject *drag = dragObject();
+ if (!drag)
+ return;
+
+ drag->drag();
+}
+
+#endif
+
+/*! \internal */
+void Q3Table::windowActivationChange(bool oldActive)
+{
+ if (oldActive && autoScrollTimer)
+ autoScrollTimer->stop();
+
+ if (!isVisible())
+ return;
+
+ if (palette().active() != palette().inactive())
+ updateContents();
+}
+
+/*!
+ \internal
+*/
+void Q3Table::setEnabled(bool b)
+{
+ if (!b) {
+ // editor will lose focus, causing a crash deep in setEnabled(),
+ // so we'll end the edit early.
+ endEdit(editRow, editCol, true, edMode != Editing);
+ }
+ Q3ScrollView::setEnabled(b);
+}
+
+
+/*
+ \class Q3TableHeader
+ \brief The Q3TableHeader class allows for creation and manipulation
+ of table headers.
+
+ \compat
+
+ Q3Table uses this subclass of Q3Header for its headers. Q3Table has a
+ horizontalHeader() for displaying column labels, and a
+ verticalHeader() for displaying row labels.
+
+*/
+
+/*
+ \enum Q3TableHeader::SectionState
+
+ This enum type denotes the state of the header's text
+
+ \value Normal the default
+ \value Bold
+ \value Selected typically represented by showing the section "sunken"
+ or "pressed in"
+*/
+
+/*!
+ Creates a new table header called \a name with \a i sections. It
+ is a child of widget \a parent and attached to table \a t.
+*/
+
+Q3TableHeader::Q3TableHeader(int i, Q3Table *t,
+ QWidget *parent, const char *name)
+ : Q3Header(i, parent, name), mousePressed(false), startPos(-1),
+ table(t), caching(false), resizedSection(-1),
+ numStretches(0)
+{
+ setIsATableHeader(true);
+ d = 0;
+ states.resize(i);
+ stretchable.resize(i);
+ states.fill(Normal, -1);
+ stretchable.fill(false, -1);
+ autoScrollTimer = new QTimer(this);
+ connect(autoScrollTimer, SIGNAL(timeout()),
+ this, SLOT(doAutoScroll()));
+#ifndef NO_LINE_WIDGET
+ line1 = new QWidget(table->viewport(), "qt_line1");
+ line1->hide();
+ line1->setBackgroundMode(PaletteText);
+ table->addChild(line1);
+ line2 = new QWidget(table->viewport(), "qt_line2");
+ line2->hide();
+ line2->setBackgroundMode(PaletteText);
+ table->addChild(line2);
+#else
+ d = new Q3TableHeaderPrivate;
+ d->oldLinePos = -1; //outside, in contents coords
+#endif
+ connect(this, SIGNAL(sizeChange(int,int,int)),
+ this, SLOT(sectionWidthChanged(int,int,int)));
+ connect(this, SIGNAL(indexChange(int,int,int)),
+ this, SLOT(indexChanged(int,int,int)));
+
+ stretchTimer = new QTimer(this);
+ widgetStretchTimer = new QTimer(this);
+ connect(stretchTimer, SIGNAL(timeout()),
+ this, SLOT(updateStretches()));
+ connect(widgetStretchTimer, SIGNAL(timeout()),
+ this, SLOT(updateWidgetStretches()));
+ startPos = -1;
+}
+
+/*!
+ Adds a new section, \a size pixels wide (or high for vertical
+ headers) with the label \a s. If \a size is negative the section's
+ size is calculated based on the width (or height) of the label's
+ text.
+*/
+
+void Q3TableHeader::addLabel(const QString &s , int size)
+{
+ Q3Header::addLabel(s, size);
+ if (count() > (int)states.size()) {
+ int s = states.size();
+ states.resize(count());
+ stretchable.resize(count());
+ for (; s < count(); ++s) {
+ states[ s ] = Normal;
+ stretchable[ s ] = false;
+ }
+ }
+}
+
+void Q3TableHeader::removeLabel(int section)
+{
+ Q3Header::removeLabel(section);
+ if (section == (int)states.size() - 1) {
+ states.resize(states.size() - 1);
+ stretchable.resize(stretchable.size() - 1);
+ }
+}
+
+void Q3TableHeader::resizeArrays(int n)
+{
+ int old = states.size();
+ states.resize(n);
+ stretchable.resize(n);
+ if (n > old) {
+ for (int i = old; i < n; ++i) {
+ stretchable[ i ] = false;
+ states[ i ] = Normal;
+ }
+ }
+}
+
+void Q3TableHeader::setLabel(int section, const QString & s, int size)
+{
+ Q3Header::setLabel(section, s, size);
+ sectionLabelChanged(section);
+}
+
+void Q3TableHeader::setLabel(int section, const QIconSet & iconset,
+ const QString & s, int size)
+{
+ Q3Header::setLabel(section, iconset, s, size);
+ sectionLabelChanged(section);
+}
+
+/*!
+ Sets the SectionState of section \a s to \a astate.
+
+ \sa sectionState()
+*/
+
+void Q3TableHeader::setSectionState(int s, SectionState astate)
+{
+ if (s < 0 || s >= (int)states.count())
+ return;
+ if (states.data()[ s ] == astate)
+ return;
+ if (isRowSelection(table->selectionMode()) && orientation() == Horizontal)
+ return;
+
+ states.data()[ s ] = astate;
+ if (updatesEnabled()) {
+ if (orientation() == Horizontal)
+ repaint(sectionPos(s) - offset(), 0, sectionSize(s), height(), false);
+ else
+ repaint(0, sectionPos(s) - offset(), width(), sectionSize(s), false);
+ }
+}
+
+void Q3TableHeader::setSectionStateToAll(SectionState state)
+{
+ if (isRowSelection(table->selectionMode()) && orientation() == Horizontal)
+ return;
+
+ register int *d = (int *) states.data();
+ int n = count();
+
+ while (n >= 4) {
+ d[0] = state;
+ d[1] = state;
+ d[2] = state;
+ d[3] = state;
+ d += 4;
+ n -= 4;
+ }
+
+ if (n > 0) {
+ d[0] = state;
+ if (n > 1) {
+ d[1] = state;
+ if (n > 2) {
+ d[2] = state;
+ }
+ }
+ }
+}
+
+/*!
+ Returns the SectionState of section \a s.
+
+ \sa setSectionState()
+*/
+
+Q3TableHeader::SectionState Q3TableHeader::sectionState(int s) const
+{
+ return (s < 0 || s >= (int)states.count() ? Normal : (Q3TableHeader::SectionState)states[s]);
+}
+
+/*! \reimp
+*/
+
+void Q3TableHeader::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ p.setPen(colorGroup().buttonText());
+ int pos = orientation() == Horizontal
+ ? e->rect().left()
+ : e->rect().top();
+ int id = mapToIndex(sectionAt(pos + offset()));
+ if (id < 0) {
+ if (pos > 0)
+ return;
+ else
+ id = 0;
+ }
+
+ QRegion reg = e->region();
+ for (int i = id; i < count(); i++) {
+ QRect r = sRect(i);
+ reg -= r;
+ p.save();
+ if (!(orientation() == Horizontal && isRowSelection(table->selectionMode())) &&
+ (sectionState(i) == Bold || sectionState(i) == Selected)) {
+ QFont f(font());
+ f.setBold(true);
+ p.setFont(f);
+ }
+ paintSection(&p, i, r);
+ p.restore();
+ if ((orientation() == Horizontal && r. right() >= e->rect().right())
+ || (orientation() == Vertical && r. bottom() >= e->rect().bottom()))
+ return;
+ }
+ p.end();
+ if (!reg.isEmpty())
+ erase(reg);
+}
+
+/*!
+ \reimp
+
+ Paints the header section with index \a index into the rectangular
+ region \a fr on the painter \a p.
+*/
+
+void Q3TableHeader::paintSection(QPainter *p, int index, const QRect& fr)
+{
+ int section = mapToSection(index);
+ if (section < 0 || cellSize(section) <= 0)
+ return;
+
+ if (sectionState(index) != Selected ||
+ (orientation() == Horizontal && isRowSelection(table->selectionMode()))) {
+ Q3Header::paintSection(p, index, fr);
+ } else {
+ QStyleOptionHeader opt;
+ opt.palette = palette();
+ opt.rect = fr;
+ opt.state = QStyle::State_Off | (orient == Qt::Horizontal ? QStyle::State_Horizontal
+ : QStyle::State_None);
+ if (isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ if (isClickEnabled()) {
+ if (sectionState(index) == Selected) {
+ opt.state |= QStyle::State_Sunken;
+ if (!mousePressed)
+ opt.state |= QStyle::State_On;
+ }
+ }
+ if (!(opt.state & QStyle::State_Sunken))
+ opt.state |= QStyle::State_Raised;
+ style()->drawControl(QStyle::CE_HeaderSection, &opt, p, this);
+ paintSectionLabel(p, index, fr);
+ }
+}
+
+static int real_pos(const QPoint &p, Qt::Orientation o)
+{
+ if (o == Qt::Horizontal)
+ return p.x();
+ return p.y();
+}
+
+/*! \reimp
+*/
+
+void Q3TableHeader::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() != LeftButton)
+ return;
+ Q3Header::mousePressEvent(e);
+ mousePressed = true;
+ pressPos = real_pos(e->pos(), orientation());
+ if (!table->currentSel || (e->state() & ShiftButton) != ShiftButton)
+ startPos = -1;
+ setCaching(true);
+ resizedSection = -1;
+#ifdef QT_NO_CURSOR
+ isResizing = false;
+#else
+ isResizing = cursor().shape() != ArrowCursor;
+ if (!isResizing && sectionAt(pressPos) != -1)
+ doSelection(e);
+#endif
+}
+
+/*! \reimp
+*/
+
+void Q3TableHeader::mouseMoveEvent(QMouseEvent *e)
+{
+ if ((e->state() & MouseButtonMask) != LeftButton // Using LeftButton simulates old behavior.
+#ifndef QT_NO_CURSOR
+ || cursor().shape() != ArrowCursor
+#endif
+ || ((e->state() & ControlButton) == ControlButton &&
+ (orientation() == Horizontal
+ ? table->columnMovingEnabled() : table->rowMovingEnabled()))) {
+ Q3Header::mouseMoveEvent(e);
+ return;
+ }
+
+ if (!doSelection(e))
+ Q3Header::mouseMoveEvent(e);
+}
+
+bool Q3TableHeader::doSelection(QMouseEvent *e)
+{
+ int p = real_pos(e->pos(), orientation()) + offset();
+
+ if (isRowSelection(table->selectionMode())) {
+ if (orientation() == Horizontal)
+ return true;
+ if (table->selectionMode() == Q3Table::SingleRow) {
+ int secAt = sectionAt(p);
+ if (secAt == -1)
+ return true;
+ table->setCurrentCell(secAt, table->currentColumn());
+ return true;
+ }
+ }
+
+ if (startPos == -1) {
+ int secAt = sectionAt(p);
+ if (((e->state() & ControlButton) != ControlButton && (e->state() & ShiftButton) != ShiftButton)
+ || table->selectionMode() == Q3Table::Single
+ || table->selectionMode() == Q3Table::SingleRow) {
+ startPos = p;
+ bool b = table->signalsBlocked();
+ table->blockSignals(true);
+ table->clearSelection();
+ table->blockSignals(b);
+ }
+ saveStates();
+
+ if (table->selectionMode() != Q3Table::NoSelection) {
+ startPos = p;
+ Q3TableSelection *oldSelection = table->currentSel;
+
+ if (orientation() == Vertical) {
+ if (!table->isRowSelected(secAt, true)) {
+ table->currentSel = new Q3TableSelection();
+ table->selections.append(table->currentSel);
+ table->currentSel->init(secAt, 0);
+ table->currentSel->expandTo(secAt, table->numCols() - 1);
+ emit table->selectionChanged();
+ }
+ table->setCurrentCell(secAt, 0);
+ } else { // orientation == Horizontal
+ if (!table->isColumnSelected(secAt, true)) {
+ table->currentSel = new Q3TableSelection();
+ table->selections.append(table->currentSel);
+ table->currentSel->init(0, secAt);
+ table->currentSel->expandTo(table->numRows() - 1, secAt);
+ emit table->selectionChanged();
+ }
+ table->setCurrentCell(0, secAt);
+ }
+
+ if ((orientation() == Horizontal && table->isColumnSelected(secAt))
+ || (orientation() == Vertical && table->isRowSelected(secAt))) {
+ setSectionState(secAt, Selected);
+ }
+
+ table->repaintSelections(oldSelection, table->currentSel,
+ orientation() == Horizontal,
+ orientation() == Vertical);
+ if (sectionAt(p) != -1)
+ endPos = p;
+
+ return true;
+ }
+ }
+
+ if (sectionAt(p) != -1)
+ endPos = p;
+ if (startPos != -1) {
+ updateSelections();
+ p -= offset();
+ if (orientation() == Horizontal && (p < 0 || p > width())) {
+ doAutoScroll();
+ autoScrollTimer->start(100, true);
+ } else if (orientation() == Vertical && (p < 0 || p > height())) {
+ doAutoScroll();
+ autoScrollTimer->start(100, true);
+ }
+ return true;
+ }
+ return table->selectionMode() == Q3Table::NoSelection;
+}
+
+static inline bool mayOverwriteMargin(int before, int after)
+{
+ /*
+ 0 is the only user value that we always respect. We also never
+ shrink a margin, in case the user wanted it that way.
+ */
+ return before != 0 && before < after;
+}
+
+void Q3TableHeader::sectionLabelChanged(int section)
+{
+ emit sectionSizeChanged(section);
+
+ // this does not really belong here
+ if (orientation() == Horizontal) {
+ int h = sizeHint().height();
+ if (h != height() && mayOverwriteMargin(table->topMargin(), h))
+ table->setTopMargin(h);
+ } else {
+ int w = sizeHint().width();
+ if (w != width() && mayOverwriteMargin((QApplication::reverseLayout() ? table->rightMargin() : table->leftMargin()), w))
+ table->setLeftMargin(w);
+ }
+}
+
+/*! \reimp */
+void Q3TableHeader::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() != LeftButton)
+ return;
+ autoScrollTimer->stop();
+ mousePressed = false;
+ setCaching(false);
+ Q3Header::mouseReleaseEvent(e);
+#ifndef NO_LINE_WIDGET
+ line1->hide();
+ line2->hide();
+#else
+ if (d->oldLinePos >= 0)
+ if (orientation() == Horizontal)
+ table->updateContents(d->oldLinePos, table->contentsY(),
+ 1, table->visibleHeight());
+ else
+ table->updateContents( table->contentsX(), d->oldLinePos,
+ table->visibleWidth(), 1);
+ d->oldLinePos = -1;
+#endif
+ if (resizedSection != -1) {
+ emit sectionSizeChanged(resizedSection);
+ updateStretches();
+ }
+
+ //Make sure all newly selected sections are painted one last time
+ QRect selectedRects;
+ for (int i = 0; i < count(); i++) {
+ if(sectionState(i) == Selected)
+ selectedRects |= sRect(i);
+ }
+ if(!selectedRects.isNull())
+ repaint(selectedRects);
+}
+
+/*! \reimp
+*/
+
+void Q3TableHeader::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (e->button() != LeftButton)
+ return;
+ if (isResizing) {
+ int p = real_pos(e->pos(), orientation()) + offset();
+ int section = sectionAt(p);
+ if (section == -1)
+ return;
+ section--;
+ if (p >= sectionPos(count() - 1) + sectionSize(count() - 1))
+ ++section;
+ while (sectionSize(section) == 0)
+ section--;
+ if (section < 0)
+ return;
+ int oldSize = sectionSize(section);
+ if (orientation() == Horizontal) {
+ table->adjustColumn(section);
+ int newSize = sectionSize(section);
+ if (oldSize != newSize)
+ emit sizeChange(section, oldSize, newSize);
+ for (int i = 0; i < table->numCols(); ++i) {
+ if (table->isColumnSelected(i) && sectionSize(i) != 0)
+ table->adjustColumn(i);
+ }
+ } else {
+ table->adjustRow(section);
+ int newSize = sectionSize(section);
+ if (oldSize != newSize)
+ emit sizeChange(section, oldSize, newSize);
+ for (int i = 0; i < table->numRows(); ++i) {
+ if (table->isRowSelected(i) && sectionSize(i) != 0)
+ table->adjustRow(i);
+ }
+ }
+ }
+}
+
+/*! \reimp
+*/
+
+void Q3TableHeader::resizeEvent(QResizeEvent *e)
+{
+ stretchTimer->stop();
+ widgetStretchTimer->stop();
+ Q3Header::resizeEvent(e);
+ if (numStretches == 0)
+ return;
+ stretchTimer->start(0, true);
+}
+
+void Q3TableHeader::updateStretches()
+{
+ if (numStretches == 0)
+ return;
+
+ int dim = orientation() == Horizontal ? width() : height();
+ if (sectionPos(count() - 1) + sectionSize(count() - 1) == dim)
+ return;
+ int i;
+ int pd = dim - (sectionPos(count() - 1)
+ + sectionSize(count() - 1));
+ bool block = signalsBlocked();
+ blockSignals(true);
+ for (i = 0; i < (int)stretchable.count(); ++i) {
+ if (!stretchable[i] ||
+ (stretchable[i] && table->d->hiddenCols[i]))
+ continue;
+ pd += sectionSize(i);
+ }
+ pd /= numStretches;
+ for (i = 0; i < (int)stretchable.count(); ++i) {
+ if (!stretchable[i] ||
+ (stretchable[i] && table->d->hiddenCols[i]))
+ continue;
+ if (i == (int)stretchable.count() - 1 &&
+ sectionPos(i) + pd < dim)
+ pd = dim - sectionPos(i);
+ resizeSection(i, QMAX(20, pd));
+ }
+ blockSignals(block);
+ table->repaintContents(false);
+ widgetStretchTimer->start(100, true);
+}
+
+void Q3TableHeader::updateWidgetStretches()
+{
+ QSize s = table->tableSize();
+ table->resizeContents(s.width(), s.height());
+ for (int i = 0; i < table->numCols(); ++i)
+ table->updateColWidgets(i);
+}
+
+void Q3TableHeader::updateSelections()
+{
+ if (table->selectionMode() == Q3Table::NoSelection ||
+ (isRowSelection(table->selectionMode()) && orientation() != Vertical ))
+ return;
+ int a = sectionAt(startPos);
+ int b = sectionAt(endPos);
+ int start = QMIN(a, b);
+ int end = QMAX(a, b);
+ register int *s = states.data();
+ for (int i = 0; i < count(); ++i) {
+ if (i < start || i > end)
+ *s = oldStates.data()[ i ];
+ else
+ *s = Selected;
+ ++s;
+ }
+ repaint(false);
+
+ if (table->currentSel) {
+ Q3TableSelection oldSelection = *table->currentSel;
+ if (orientation() == Vertical)
+ table->currentSel->expandTo(b, table->horizontalHeader()->count() - 1);
+ else
+ table->currentSel->expandTo(table->verticalHeader()->count() - 1, b);
+ table->repaintSelections(&oldSelection, table->currentSel,
+ orientation() == Horizontal,
+ orientation() == Vertical);
+ }
+ emit table->selectionChanged();
+}
+
+void Q3TableHeader::saveStates()
+{
+ oldStates.resize(count());
+ register int *s = states.data();
+ register int *s2 = oldStates.data();
+ for (int i = 0; i < count(); ++i) {
+ *s2 = *s;
+ ++s2;
+ ++s;
+ }
+}
+
+void Q3TableHeader::doAutoScroll()
+{
+ QPoint pos = mapFromGlobal(QCursor::pos());
+ int p = real_pos(pos, orientation()) + offset();
+ if (sectionAt(p) != -1)
+ endPos = p;
+ if (orientation() == Horizontal)
+ table->ensureVisible(endPos, table->contentsY());
+ else
+ table->ensureVisible(table->contentsX(), endPos);
+ updateSelections();
+ autoScrollTimer->start(100, true);
+}
+
+void Q3TableHeader::sectionWidthChanged(int col, int, int)
+{
+ resizedSection = col;
+ if (orientation() == Horizontal) {
+#ifndef NO_LINE_WIDGET
+ table->moveChild(line1, Q3Header::sectionPos(col) - 1,
+ table->contentsY());
+ line1->resize(1, table->visibleHeight());
+ line1->show();
+ line1->raise();
+ table->moveChild(line2,
+ Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1,
+ table->contentsY());
+ line2->resize(1, table->visibleHeight());
+ line2->show();
+ line2->raise();
+#else
+ QPainter p(table->viewport());
+ int lx = Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1;
+ int ly = table->contentsY();
+
+ if (lx != d->oldLinePos) {
+ QPoint pt = table->contentsToViewport(QPoint(lx, ly));
+ p.drawLine(pt.x(), pt.y()+1,
+ pt.x(), pt.y()+ table->visibleHeight());
+ if (d->oldLinePos >= 0)
+ table->repaintContents(d->oldLinePos, table->contentsY(),
+ 1, table->visibleHeight());
+
+ d->oldLinePos = lx;
+ }
+#endif
+ } else {
+#ifndef NO_LINE_WIDGET
+ table->moveChild(line1, table->contentsX(),
+ Q3Header::sectionPos(col) - 1);
+ line1->resize(table->visibleWidth(), 1);
+ line1->show();
+ line1->raise();
+ table->moveChild(line2, table->contentsX(),
+ Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1);
+ line2->resize(table->visibleWidth(), 1);
+ line2->show();
+ line2->raise();
+
+#else
+ QPainter p(table->viewport());
+ int lx = table->contentsX();
+ int ly = Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1;
+
+ if (ly != d->oldLinePos) {
+ QPoint pt = table->contentsToViewport(QPoint(lx, ly));
+ p.drawLine(pt.x()+1, pt.y(),
+ pt.x() + table->visibleWidth(), pt.y());
+ if (d->oldLinePos >= 0)
+ table->repaintContents( table->contentsX(), d->oldLinePos,
+ table->visibleWidth(), 1);
+ d->oldLinePos = ly;
+ }
+
+#endif
+ }
+}
+
+/*!
+ \reimp
+
+ Returns the size of section \a section in pixels or -1 if \a
+ section is out of range.
+*/
+
+int Q3TableHeader::sectionSize(int section) const
+{
+ if (count() <= 0 || section < 0 || section >= count())
+ return -1;
+ if (caching && section < (int)sectionSizes.count())
+ return sectionSizes[ section ];
+ return Q3Header::sectionSize(section);
+}
+
+/*!
+ \reimp
+
+ Returns the start position of section \a section in pixels or -1
+ if \a section is out of range.
+
+ \sa sectionAt()
+*/
+
+int Q3TableHeader::sectionPos(int section) const
+{
+ if (count() <= 0 || section < 0 || section >= count())
+ return -1;
+ if (caching && section < (int)sectionPoses.count())
+ return sectionPoses[ section ];
+ return Q3Header::sectionPos(section);
+}
+
+/*!
+ \reimp
+
+ Returns the number of the section at index position \a pos or -1
+ if there is no section at the position given.
+
+ \sa sectionPos()
+*/
+
+int Q3TableHeader::sectionAt(int pos) const
+{
+ if (!caching || sectionSizes.count() <= 0 || sectionPoses.count() <= 0)
+ return Q3Header::sectionAt(pos);
+ if (count() <= 0 || pos > sectionPoses[ count() - 1 ] + sectionSizes[ count() - 1 ])
+ return -1;
+ int l = 0;
+ int r = count() - 1;
+ int i = ((l+r+1) / 2);
+ while (r - l) {
+ if (sectionPoses[i] > pos)
+ r = i -1;
+ else
+ l = i;
+ i = ((l+r+1) / 2);
+ }
+ if (sectionPoses[i] <= pos &&
+ pos <= sectionPoses[i] + sectionSizes[ mapToSection(i) ])
+ return mapToSection(i);
+ return -1;
+}
+
+void Q3TableHeader::updateCache()
+{
+ sectionPoses.resize(count());
+ sectionSizes.resize(count());
+ if (!caching)
+ return;
+ for (int i = 0; i < count(); ++i) {
+ sectionSizes[ i ] = Q3Header::sectionSize(i);
+ sectionPoses[ i ] = Q3Header::sectionPos(i);
+ }
+}
+
+void Q3TableHeader::setCaching(bool b)
+{
+ if (caching == b)
+ return;
+ caching = b;
+ sectionPoses.resize(count());
+ sectionSizes.resize(count());
+ if (b) {
+ for (int i = 0; i < count(); ++i) {
+ sectionSizes[ i ] = Q3Header::sectionSize(i);
+ sectionPoses[ i ] = Q3Header::sectionPos(i);
+ }
+ }
+}
+
+/*!
+ If \a b is true, section \a s is stretchable; otherwise the
+ section is not stretchable.
+
+ \sa isSectionStretchable()
+*/
+
+void Q3TableHeader::setSectionStretchable(int s, bool b)
+{
+ if (stretchable[ s ] == b)
+ return;
+ stretchable[ s ] = b;
+ if (b)
+ numStretches++;
+ else
+ numStretches--;
+}
+
+/*!
+ Returns true if section \a s is stretcheable; otherwise returns
+ false.
+
+ \sa setSectionStretchable()
+*/
+
+bool Q3TableHeader::isSectionStretchable(int s) const
+{
+ return stretchable[ s ];
+}
+
+void Q3TableHeader::swapSections(int oldIdx, int newIdx, bool swapTable)
+{
+ extern bool qt_qheader_label_return_null_strings; // qheader.cpp
+ qt_qheader_label_return_null_strings = true;
+
+ QIconSet oldIconSet, newIconSet;
+ if (iconSet(oldIdx))
+ oldIconSet = *iconSet(oldIdx);
+ if (iconSet(newIdx))
+ newIconSet = *iconSet(newIdx);
+ QString oldLabel = label(oldIdx);
+ QString newLabel = label(newIdx);
+ bool sectionsHasContent = !(oldIconSet.isNull() && newIconSet.isNull()
+ && oldLabel.isNull() && newLabel.isNull());
+ if (sectionsHasContent) {
+ Q3HeaderData *data = static_cast<Q3Header*>(this)->d;
+ bool oldNullLabel = qt_get_null_label_bit(data, oldIdx);
+ bool newNullLabel = qt_get_null_label_bit(data, newIdx);
+ setLabel(oldIdx, newIconSet, newLabel);
+ setLabel(newIdx, oldIconSet, oldLabel);
+ qt_set_null_label_bit(data, oldIdx, newNullLabel);
+ qt_set_null_label_bit(data, newIdx, oldNullLabel);
+ }
+
+ qt_qheader_label_return_null_strings = false;
+
+ int w1 = sectionSize(oldIdx);
+ int w2 = sectionSize(newIdx);
+ if (w1 != w2) {
+ resizeSection(oldIdx, w2);
+ resizeSection(newIdx, w1);
+ }
+
+ if (!swapTable)
+ return;
+ if (orientation() == Horizontal)
+ table->swapColumns(oldIdx, newIdx);
+ else
+ table->swapRows(oldIdx, newIdx);
+}
+
+void Q3TableHeader::indexChanged(int sec, int oldIdx, int newIdx)
+{
+ newIdx = mapToIndex(sec);
+ if (oldIdx > newIdx)
+ moveSection(sec, oldIdx + 1);
+ else
+ moveSection(sec, oldIdx);
+
+ if (oldIdx < newIdx) {
+ while (oldIdx < newIdx) {
+ swapSections(oldIdx, oldIdx + 1);
+ oldIdx++;
+ }
+ } else {
+ while (oldIdx > newIdx) {
+ swapSections(oldIdx - 1, oldIdx);
+ oldIdx--;
+ }
+ }
+
+ table->repaintContents(table->contentsX(), table->contentsY(),
+ table->visibleWidth(), table->visibleHeight());
+}
+
+void Q3TableHeader::setLabels(const QStringList & labels)
+{
+ int i = 0;
+ const int c = QMIN(count(), (int)labels.count());
+ bool updates = updatesEnabled();
+ if (updates)
+ setUpdatesEnabled(false);
+ for (QStringList::ConstIterator it = labels.begin(); i < c; ++i, ++it) {
+ if (i == c - 1) {
+ if (updates)
+ setUpdatesEnabled(true);
+ setLabel(i, *it);
+ } else {
+ Q3Header::setLabel(i, *it);
+ emit sectionSizeChanged(i);
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "q3table.moc"
diff --git a/src/qt3support/itemviews/q3table.h b/src/qt3support/itemviews/q3table.h
new file mode 100644
index 0000000..5e44e09
--- /dev/null
+++ b/src/qt3support/itemviews/q3table.h
@@ -0,0 +1,548 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TABLE_H
+#define Q3TABLE_H
+
+#include <Qt3Support/q3scrollview.h>
+#include <QtGui/qpixmap.h>
+#include <Qt3Support/q3ptrvector.h>
+#include <Qt3Support/q3header.h>
+#include <Qt3Support/q3memarray.h>
+#include <Qt3Support/q3ptrlist.h>
+#include <Qt3Support/q3shared.h>
+#include <Qt3Support/q3intdict.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+class Q3TableHeader;
+class QValidator;
+class Q3Table;
+class QPaintEvent;
+class QTimer;
+class QResizeEvent;
+class Q3ComboBox;
+class QCheckBox;
+class Q3DragObject;
+class QColorGroup;
+
+struct Q3TablePrivate;
+struct Q3TableHeaderPrivate;
+
+
+class Q_COMPAT_EXPORT Q3TableSelection
+{
+public:
+ Q3TableSelection();
+ Q3TableSelection(int start_row, int start_col, int end_row, int end_col);
+ void init(int row, int col);
+ void expandTo(int row, int col);
+ bool operator==(const Q3TableSelection &s) const;
+ bool operator!=(const Q3TableSelection &s) const { return !(operator==(s)); }
+
+ int topRow() const { return tRow; }
+ int bottomRow() const { return bRow; }
+ int leftCol() const { return lCol; }
+ int rightCol() const { return rCol; }
+ int anchorRow() const { return aRow; }
+ int anchorCol() const { return aCol; }
+ int numRows() const;
+ int numCols() const;
+
+ bool isActive() const { return active; }
+ bool isEmpty() const { return numRows() == 0; }
+
+private:
+ uint active : 1;
+ uint inited : 1;
+ int tRow, lCol, bRow, rCol;
+ int aRow, aCol;
+};
+
+class Q_COMPAT_EXPORT Q3TableItem
+{
+ friend class Q3Table;
+
+public:
+ enum EditType { Never, OnTyping, WhenCurrent, Always };
+
+ Q3TableItem(Q3Table *table, EditType et);
+ Q3TableItem(Q3Table *table, EditType et, const QString &text);
+ Q3TableItem(Q3Table *table, EditType et, const QString &text,
+ const QPixmap &p);
+ virtual ~Q3TableItem();
+
+ virtual QPixmap pixmap() const;
+ virtual QString text() const;
+ virtual void setPixmap(const QPixmap &p);
+ virtual void setText(const QString &t);
+ Q3Table *table() const { return t; }
+
+ virtual int alignment() const;
+ virtual void setWordWrap(bool b);
+ bool wordWrap() const;
+
+ EditType editType() const;
+ virtual QWidget *createEditor() const;
+ virtual void setContentFromEditor(QWidget *w);
+ virtual void setReplaceable(bool);
+ bool isReplaceable() const;
+
+ virtual QString key() const;
+ virtual QSize sizeHint() const;
+
+ virtual void setSpan(int rs, int cs);
+ int rowSpan() const;
+ int colSpan() const;
+
+ virtual void setRow(int r);
+ virtual void setCol(int c);
+ int row() const;
+ int col() const;
+
+ virtual void paint(QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected);
+
+ void updateEditor(int oldRow, int oldCol);
+
+ virtual void setEnabled(bool b);
+ bool isEnabled() const;
+
+ virtual int rtti() const;
+ static int RTTI;
+
+private:
+ QString txt;
+ QPixmap pix;
+ Q3Table *t;
+ EditType edType;
+ uint wordwrap : 1;
+ uint tcha : 1;
+ uint enabled : 1;
+ int rw, cl;
+ int rowspan, colspan;
+};
+
+class Q_COMPAT_EXPORT Q3ComboTableItem : public Q3TableItem
+{
+public:
+ Q3ComboTableItem(Q3Table *table, const QStringList &list, bool editable = false);
+ ~Q3ComboTableItem();
+ virtual QWidget *createEditor() const;
+ virtual void setContentFromEditor(QWidget *w);
+ virtual void paint(QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected);
+ virtual void setCurrentItem(int i);
+ virtual void setCurrentItem(const QString &i);
+ int currentItem() const;
+ QString currentText() const;
+ int count() const;
+#if !defined(Q_NO_USING_KEYWORD)
+ using Q3TableItem::text;
+#else
+ inline QString text() const { return Q3TableItem::text(); }
+#endif
+ QString text(int i) const;
+ virtual void setEditable(bool b);
+ bool isEditable() const;
+ virtual void setStringList(const QStringList &l);
+
+ int rtti() const;
+ static int RTTI;
+
+ QSize sizeHint() const;
+
+private:
+ Q3ComboBox *cb;
+ QStringList entries;
+ int current;
+ bool edit;
+ static Q3ComboBox *fakeCombo;
+ static QWidget *fakeComboWidget;
+ static int fakeRef;
+
+};
+
+class Q_COMPAT_EXPORT Q3CheckTableItem : public Q3TableItem
+{
+public:
+ Q3CheckTableItem(Q3Table *table, const QString &txt);
+ void setText(const QString &t);
+ virtual QWidget *createEditor() const;
+ virtual void setContentFromEditor(QWidget *w);
+ virtual void paint(QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected);
+ virtual void setChecked(bool b);
+ bool isChecked() const;
+
+ int rtti() const;
+ static int RTTI;
+
+ QSize sizeHint() const;
+
+private:
+ QCheckBox *cb;
+ bool checked;
+
+};
+
+class Q_COMPAT_EXPORT Q3Table : public Q3ScrollView
+{
+ Q_OBJECT
+ Q_ENUMS(SelectionMode FocusStyle)
+ Q_PROPERTY(int numRows READ numRows WRITE setNumRows)
+ Q_PROPERTY(int numCols READ numCols WRITE setNumCols)
+ Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid)
+ Q_PROPERTY(bool rowMovingEnabled READ rowMovingEnabled WRITE setRowMovingEnabled)
+ Q_PROPERTY(bool columnMovingEnabled READ columnMovingEnabled WRITE setColumnMovingEnabled)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(bool sorting READ sorting WRITE setSorting)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode)
+ Q_PROPERTY(FocusStyle focusStyle READ focusStyle WRITE setFocusStyle)
+ Q_PROPERTY(int numSelections READ numSelections)
+
+ friend class Q3TableHeader;
+ friend class Q3ComboTableItem;
+ friend class Q3CheckTableItem;
+ friend class Q3TableItem;
+
+public:
+ Q3Table(QWidget* parent=0, const char* name=0);
+ Q3Table(int numRows, int numCols,
+ QWidget* parent=0, const char* name=0);
+ ~Q3Table();
+
+ Q3Header *horizontalHeader() const;
+ Q3Header *verticalHeader() const;
+
+ enum SelectionMode { Single, Multi, SingleRow, MultiRow, NoSelection };
+ virtual void setSelectionMode(SelectionMode mode);
+ SelectionMode selectionMode() const;
+
+ virtual void setItem(int row, int col, Q3TableItem *item);
+ virtual void setText(int row, int col, const QString &text);
+ virtual void setPixmap(int row, int col, const QPixmap &pix);
+ virtual Q3TableItem *item(int row, int col) const;
+ virtual QString text(int row, int col) const;
+ virtual QPixmap pixmap(int row, int col) const;
+ virtual void clearCell(int row, int col);
+
+ virtual QRect cellGeometry(int row, int col) const;
+ virtual int columnWidth(int col) const;
+ virtual int rowHeight(int row) const;
+ virtual int columnPos(int col) const;
+ virtual int rowPos(int row) const;
+ virtual int columnAt(int x) const;
+ virtual int rowAt(int y) const;
+
+ virtual int numRows() const;
+ virtual int numCols() const;
+
+ void updateCell(int row, int col);
+
+ bool eventFilter(QObject * o, QEvent *);
+
+ int currentRow() const { return curRow; }
+ int currentColumn() const { return curCol; }
+ void ensureCellVisible(int row, int col);
+
+ bool isSelected(int row, int col) const;
+ bool isRowSelected(int row, bool full = false) const;
+ bool isColumnSelected(int col, bool full = false) const;
+ int numSelections() const;
+ Q3TableSelection selection(int num) const;
+ virtual int addSelection(const Q3TableSelection &s);
+ virtual void removeSelection(const Q3TableSelection &s);
+ virtual void removeSelection(int num);
+ virtual int currentSelection() const;
+
+ void selectCells(int start_row, int start_col, int end_row, int end_col);
+ virtual void selectRow(int row);
+ virtual void selectColumn(int col);
+
+ bool showGrid() const;
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ bool columnMovingEnabled() const;
+ bool rowMovingEnabled() const;
+
+ virtual void sortColumn(int col, bool ascending = true,
+ bool wholeRows = false);
+ bool sorting() const;
+
+ virtual void takeItem(Q3TableItem *i);
+
+ virtual void setCellWidget(int row, int col, QWidget *e);
+ virtual QWidget *cellWidget(int row, int col) const;
+ virtual void clearCellWidget(int row, int col);
+
+ virtual QRect cellRect(int row, int col) const;
+
+ virtual void paintCell(QPainter *p, int row, int col,
+ const QRect &cr, bool selected);
+ virtual void paintCell(QPainter *p, int row, int col,
+ const QRect &cr, bool selected, const QColorGroup &cg);
+ virtual void paintFocus(QPainter *p, const QRect &r);
+ QSize sizeHint() const;
+
+ bool isReadOnly() const;
+ bool isRowReadOnly(int row) const;
+ bool isColumnReadOnly(int col) const;
+
+ void setEnabled(bool b);
+
+ void repaintSelections();
+
+ enum FocusStyle { FollowStyle, SpreadSheet };
+ virtual void setFocusStyle(FocusStyle fs);
+ FocusStyle focusStyle() const;
+
+ void updateHeaderStates();
+
+ bool isRowHidden(int row) const;
+ bool isColumnHidden(int col) const;
+ bool isColumnStretchable(int col) const;
+ bool isRowStretchable(int row) const;
+ bool dragEnabled() const;
+
+public Q_SLOTS:
+ virtual void setNumRows(int r);
+ virtual void setNumCols(int r);
+ virtual void setShowGrid(bool b);
+ virtual void hideRow(int row);
+ virtual void hideColumn(int col);
+ virtual void showRow(int row);
+ virtual void showColumn(int col);
+
+ virtual void setColumnWidth(int col, int w);
+ virtual void setRowHeight(int row, int h);
+
+ virtual void adjustColumn(int col);
+ virtual void adjustRow(int row);
+
+ virtual void setColumnStretchable(int col, bool stretch);
+ virtual void setRowStretchable(int row, bool stretch);
+ virtual void setSorting(bool b);
+ virtual void swapRows(int row1, int row2, bool swapHeader = false);
+ virtual void swapColumns(int col1, int col2, bool swapHeader = false);
+ virtual void swapCells(int row1, int col1, int row2, int col2);
+
+ virtual void setLeftMargin(int m);
+ virtual void setTopMargin(int m);
+ virtual void setCurrentCell(int row, int col);
+ void clearSelection(bool repaint = true);
+ virtual void setColumnMovingEnabled(bool b);
+ virtual void setRowMovingEnabled(bool b);
+
+ virtual void setReadOnly(bool b);
+ virtual void setRowReadOnly(int row, bool ro);
+ virtual void setColumnReadOnly(int col, bool ro);
+
+ virtual void setDragEnabled(bool b);
+
+ virtual void insertRows(int row, int count = 1);
+ virtual void insertColumns(int col, int count = 1);
+ virtual void removeRow(int row);
+ virtual void removeRows(const Q3MemArray<int> &rows);
+ virtual void removeColumn(int col);
+ virtual void removeColumns(const Q3MemArray<int> &cols);
+
+ virtual void editCell(int row, int col, bool replace = false);
+
+ void setRowLabels(const QStringList &labels);
+ void setColumnLabels(const QStringList &labels);
+
+protected:
+ enum EditMode { NotEditing, Editing, Replacing };
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ void contentsMousePressEvent(QMouseEvent*);
+ void contentsMouseMoveEvent(QMouseEvent*);
+ void contentsMouseDoubleClickEvent(QMouseEvent*);
+ void contentsMouseReleaseEvent(QMouseEvent*);
+ void contentsContextMenuEvent(QContextMenuEvent * e);
+ void keyPressEvent(QKeyEvent*);
+ void focusInEvent(QFocusEvent*);
+ void focusOutEvent(QFocusEvent*);
+ void viewportResizeEvent(QResizeEvent *);
+ void showEvent(QShowEvent *e);
+ void paintEvent(QPaintEvent *e);
+ void setEditMode(EditMode mode, int row, int col);
+#ifndef QT_NO_DRAGANDDROP
+ virtual void contentsDragEnterEvent(QDragEnterEvent *e);
+ virtual void contentsDragMoveEvent(QDragMoveEvent *e);
+ virtual void contentsDragLeaveEvent(QDragLeaveEvent *e);
+ virtual void contentsDropEvent(QDropEvent *e);
+ virtual Q3DragObject *dragObject();
+ virtual void startDrag();
+#endif
+
+ virtual void paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch);
+ virtual void activateNextCell();
+ virtual QWidget *createEditor(int row, int col, bool initFromCell) const;
+ virtual void setCellContentFromEditor(int row, int col);
+ virtual QWidget *beginEdit(int row, int col, bool replace);
+ virtual void endEdit(int row, int col, bool accept, bool replace);
+
+ virtual void resizeData(int len);
+ virtual void insertWidget(int row, int col, QWidget *w);
+ int indexOf(int row, int col) const;
+
+ void windowActivationChange(bool);
+ bool isEditing() const;
+ EditMode editMode() const;
+ int currEditRow() const;
+ int currEditCol() const;
+
+protected Q_SLOTS:
+ virtual void columnWidthChanged(int col);
+ virtual void rowHeightChanged(int row);
+ virtual void columnIndexChanged(int section, int fromIndex, int toIndex);
+ virtual void rowIndexChanged(int section, int fromIndex, int toIndex);
+ virtual void columnClicked(int col);
+
+Q_SIGNALS:
+ void currentChanged(int row, int col);
+ void clicked(int row, int col, int button, const QPoint &mousePos);
+ void doubleClicked(int row, int col, int button, const QPoint &mousePos);
+ void pressed(int row, int col, int button, const QPoint &mousePos);
+ void selectionChanged();
+ void valueChanged(int row, int col);
+ void contextMenuRequested(int row, int col, const QPoint &pos);
+#ifndef QT_NO_DRAGANDDROP
+ void dropped(QDropEvent *e);
+#endif
+
+private Q_SLOTS:
+ void doAutoScroll();
+ void doValueChanged();
+ void updateGeometriesSlot();
+
+private:
+ void contentsMousePressEventEx(QMouseEvent*);
+ void drawContents(QPainter*);
+ void updateGeometries();
+ void repaintSelections(Q3TableSelection *oldSelection,
+ Q3TableSelection *newSelection,
+ bool updateVertical = true,
+ bool updateHorizontal = true);
+ QRect rangeGeometry(int topRow, int leftCol,
+ int bottomRow, int rightCol, bool &optimize);
+ void fixRow(int &row, int y);
+ void fixCol(int &col, int x);
+
+ void init(int numRows, int numCols);
+ QSize tableSize() const;
+ void repaintCell(int row, int col);
+ void contentsToViewport2(int x, int y, int& vx, int& vy);
+ QPoint contentsToViewport2(const QPoint &p);
+ void viewportToContents2(int vx, int vy, int& x, int& y);
+ QPoint viewportToContents2(const QPoint &p);
+
+ void updateRowWidgets(int row);
+ void updateColWidgets(int col);
+ bool isSelected(int row, int col, bool includeCurrent) const;
+ void setCurrentCell(int row, int col, bool updateSelections, bool ensureVisible = false);
+ void fixCell(int &row, int &col, int key);
+ void delayedUpdateGeometries();
+ struct TableWidget
+ {
+ TableWidget(QWidget *w, int r, int c) : wid(w), row(r), col (c) {}
+ QWidget *wid;
+ int row, col;
+ };
+ void saveContents(Q3PtrVector<Q3TableItem> &tmp,
+ Q3PtrVector<TableWidget> &tmp2);
+ void updateHeaderAndResizeContents(Q3TableHeader *header,
+ int num, int colRow,
+ int width, bool &updateBefore);
+ void restoreContents(Q3PtrVector<Q3TableItem> &tmp,
+ Q3PtrVector<TableWidget> &tmp2);
+ void finishContentsResze(bool updateBefore);
+
+private:
+ Q3PtrVector<Q3TableItem> contents;
+ Q3PtrVector<QWidget> widgets;
+ int curRow;
+ int curCol;
+ Q3TableHeader *leftHeader, *topHeader;
+ EditMode edMode;
+ int editCol, editRow;
+ Q3PtrList<Q3TableSelection> selections;
+ Q3TableSelection *currentSel;
+ QTimer *autoScrollTimer;
+ int lastSortCol;
+ bool sGrid : 1;
+ bool mRows : 1;
+ bool mCols : 1;
+ bool asc : 1;
+ bool doSort : 1;
+ bool unused : 1;
+ bool readOnly : 1;
+ bool shouldClearSelection : 1;
+ bool dEnabled : 1;
+ bool context_menu : 1;
+ bool drawActiveSelection : 1;
+ bool was_visible : 1;
+ SelectionMode selMode;
+ int pressedRow, pressedCol;
+ Q3TablePrivate *d;
+ Q3IntDict<int> roRows;
+ Q3IntDict<int> roCols;
+ int startDragRow;
+ int startDragCol;
+ QPoint dragStartPos;
+ int oldCurrentRow, oldCurrentCol;
+ FocusStyle focusStl;
+
+ Q_DISABLE_COPY(Q3Table)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TABLE_H
diff --git a/src/qt3support/network/network.pri b/src/qt3support/network/network.pri
new file mode 100644
index 0000000..086f56a
--- /dev/null
+++ b/src/qt3support/network/network.pri
@@ -0,0 +1,30 @@
+# Qt compat module
+
+HEADERS += network/q3dns.h \
+ network/q3ftp.h \
+ network/q3http.h \
+ network/q3localfs.h \
+ network/q3network.h \
+ network/q3networkprotocol.h \
+ network/q3socket.h \
+ network/q3socketdevice.h \
+ network/q3serversocket.h \
+ network/q3url.h \
+ network/q3urloperator.h
+
+SOURCES += network/q3dns.cpp \
+ network/q3ftp.cpp \
+ network/q3http.cpp \
+ network/q3localfs.cpp \
+ network/q3network.cpp \
+ network/q3networkprotocol.cpp \
+ network/q3socket.cpp \
+ network/q3socketdevice.cpp \
+ network/q3serversocket.cpp \
+ network/q3url.cpp \
+ network/q3urloperator.cpp
+
+win32:SOURCES += network/q3socketdevice_win.cpp
+unix:SOURCES += network/q3socketdevice_unix.cpp
+mac:LIBS_PRIVATE += -lresolv
+
diff --git a/src/qt3support/network/q3dns.cpp b/src/qt3support/network/q3dns.cpp
new file mode 100644
index 0000000..d489652
--- /dev/null
+++ b/src/qt3support/network/q3dns.cpp
@@ -0,0 +1,2598 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qbytearray.h"
+#include <private/qsystemlibrary_p.h>
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_CYGWIN)
+# include "qt_windows.h"
+#else
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <arpa/nameser.h>
+# include <resolv.h>
+extern "C" int res_init();
+#endif
+
+// POSIX Large File Support redefines open -> open64
+#if defined(open)
+# undef open
+#endif
+
+// POSIX Large File Support redefines truncate -> truncate64
+#if defined(truncate)
+# undef truncate
+#endif
+
+// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
+#if defined(connect)
+# undef connect
+#endif
+
+// UnixWare 7 redefines socket -> _socket
+#if defined(socket)
+# undef socket
+#endif
+
+#include "q3dns.h"
+
+#ifndef QT_NO_DNS
+
+#include "qdatetime.h"
+#include "q3dict.h"
+#include "q3ptrlist.h"
+#include "qstring.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "q3ptrvector.h"
+#include "q3strlist.h"
+#include "q3ptrdict.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "q3socketdevice.h"
+#include "q3cleanuphandler.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3DNS_DEBUG
+
+static Q_UINT16 theId; // ### seeded started by now()
+
+
+static QDateTime * originOfTime = 0;
+
+static Q3CleanupHandler<QDateTime> q3dns_cleanup_time;
+
+static Q_UINT32 now()
+{
+ if ( originOfTime )
+ return originOfTime->secsTo( QDateTime::currentDateTime() );
+
+ originOfTime = new QDateTime( QDateTime::currentDateTime() );
+ theId = originOfTime->time().msec() * 60 + originOfTime->time().second();
+ q3dns_cleanup_time.add( &originOfTime );
+ return 0;
+}
+
+
+static Q3PtrList<QHostAddress> * theNs = 0;
+static Q3StrList * theDomains = 0;
+static bool ipv6support = false;
+
+class Q3DnsPrivate {
+public:
+ Q3DnsPrivate() : queryTimer( 0 ), noNames(false)
+ {
+#if defined(Q_DNS_SYNCHRONOUS)
+#if defined(Q_OS_UNIX)
+ noEventLoop = qApp==0 || qApp->loopLevel()==0;
+#else
+ noEventLoop = false;
+#endif
+#endif
+ }
+ ~Q3DnsPrivate()
+ {
+ delete queryTimer;
+ }
+private:
+ QTimer * queryTimer;
+ bool noNames;
+#if defined(Q_DNS_SYNCHRONOUS)
+ bool noEventLoop;
+#endif
+
+ friend class Q3Dns;
+ friend class Q3DnsAnswer;
+};
+
+
+class Q3DnsRR;
+class Q3DnsDomain;
+
+
+
+// Q3DnsRR is the class used to store a single RR. Q3DnsRR can store
+// all of the supported RR types. a Q3DnsRR is always cached.
+
+// Q3DnsRR is mostly constructed from the outside. a but hacky, but
+// permissible since the entire class is internal.
+
+class Q3DnsRR {
+public:
+ Q3DnsRR( const QString & label );
+ ~Q3DnsRR();
+
+public:
+ Q3DnsDomain * domain;
+ Q3Dns::RecordType t;
+ bool nxdomain;
+ bool current;
+ Q_UINT32 expireTime;
+ Q_UINT32 deleteTime;
+ // somewhat space-wasting per-type data
+ // a / aaaa
+ QHostAddress address;
+ // cname / mx / srv / ptr
+ QString target;
+ // mx / srv
+ Q_UINT16 priority;
+ // srv
+ Q_UINT16 weight;
+ Q_UINT16 port;
+ // txt
+ QString text; // could be overloaded into target...
+private:
+
+};
+
+
+class Q3DnsDomain {
+public:
+ Q3DnsDomain( const QString & label );
+ ~Q3DnsDomain();
+
+ static void add( const QString & label, Q3DnsRR * );
+ static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * );
+
+ void take( Q3DnsRR * );
+
+ void sweep( Q_UINT32 thisSweep );
+
+ bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); }
+
+ QString name() const { return l; }
+
+public:
+ QString l;
+ Q3PtrList<Q3DnsRR> * rrs;
+};
+
+
+class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack
+public:
+ Q3DnsQuery():
+ id( 0 ), t( Q3Dns::None ), step(0), started(0),
+ dns( new Q3PtrDict<void>(17) ) {}
+ ~Q3DnsQuery() { delete dns; }
+ Q_UINT16 id;
+ Q3Dns::RecordType t;
+ QString l;
+
+ uint step;
+ Q_UINT32 started;
+
+ Q3PtrDict<void> * dns;
+};
+
+
+
+class Q3DnsAnswer {
+public:
+ Q3DnsAnswer( Q3DnsQuery * );
+ Q3DnsAnswer( const QByteArray &, Q3DnsQuery * );
+ ~Q3DnsAnswer();
+
+ void parse();
+ void notify();
+
+ bool ok;
+
+private:
+ Q3DnsQuery * query;
+
+ Q_UINT8 * answer;
+ int size;
+ int pp;
+
+ Q3PtrList<Q3DnsRR> * rrs;
+
+ // convenience
+ int next;
+ int ttl;
+ QString label;
+ Q3DnsRR * rr;
+
+ QString readString(bool multipleLabels = true);
+ void parseA();
+ void parseAaaa();
+ void parseMx();
+ void parseSrv();
+ void parseCname();
+ void parsePtr();
+ void parseTxt();
+ void parseNs();
+};
+
+
+Q3DnsRR::Q3DnsRR( const QString & label )
+ : domain( 0 ), t( Q3Dns::None ),
+ nxdomain( false ), current( false ),
+ expireTime( 0 ), deleteTime( 0 ),
+ priority( 0 ), weight( 0 ), port( 0 )
+{
+ Q3DnsDomain::add( label, this );
+}
+
+
+// not supposed to be deleted except by Q3DnsDomain
+Q3DnsRR::~Q3DnsRR()
+{
+ // nothing is necessary
+}
+
+
+// this one just sticks in a NXDomain
+Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ )
+{
+ ok = true;
+
+ answer = 0;
+ size = 0;
+ query = query_;
+ pp = 0;
+ rrs = new Q3PtrList<Q3DnsRR>;
+ rrs->setAutoDelete( false );
+ next = size;
+ ttl = 0;
+ label.clear();
+ rr = 0;
+
+ Q3DnsRR * newrr = new Q3DnsRR( query->l );
+ newrr->t = query->t;
+ newrr->deleteTime = query->started + 10;
+ newrr->expireTime = query->started + 10;
+ newrr->nxdomain = true;
+ newrr->current = true;
+ rrs->append( newrr );
+}
+
+
+Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_,
+ Q3DnsQuery * query_ )
+{
+ ok = true;
+
+ answer = (Q_UINT8 *)(answer_.data());
+ size = (int)answer_.size();
+ query = query_;
+ pp = 0;
+ rrs = new Q3PtrList<Q3DnsRR>;
+ rrs->setAutoDelete( false );
+ next = size;
+ ttl = 0;
+ label.clear();
+ rr = 0;
+}
+
+
+Q3DnsAnswer::~Q3DnsAnswer()
+{
+ if ( !ok && rrs ) {
+ Q3PtrListIterator<Q3DnsRR> it( *rrs );
+ Q3DnsRR * tmprr;
+ while( (tmprr=it.current()) != 0 ) {
+ ++it;
+ tmprr->t = Q3Dns::None; // will be deleted soonish
+ }
+ }
+ delete rrs;
+}
+
+
+QString Q3DnsAnswer::readString(bool multipleLabels)
+{
+ int p = pp;
+ QString r;
+ Q_UINT8 b;
+ for( ;; ) {
+ b = 128;
+ // Read one character
+ if ( p >= 0 && p < size )
+ b = answer[p];
+
+ switch( b >> 6 ) {
+ case 0:
+ // b is less than 64
+ p++;
+
+ // Detect end of data
+ if ( b == 0 ) {
+ if ( p > pp )
+ pp = p;
+ return r.isNull() ? QLatin1String( "." ) : r;
+ }
+
+ // Read a label of size 'b' characters
+ if ( !r.isNull() )
+ r += QLatin1Char('.');
+ while( b-- > 0 )
+ r += QLatin1Char( answer[p++] );
+
+ // Return immediately if we were only supposed to read one
+ // label.
+ if (!multipleLabels)
+ return r;
+
+ break;
+ default:
+ // Ignore unrecognized control character, or p was out of
+ // range.
+ goto not_ok;
+ case 3:
+ // Use the next character to determine the relative offset
+ // to jump to before continuing the packet parsing.
+ int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1];
+
+ if ( q >= pp || q >= p )
+ goto not_ok;
+ if ( p >= pp )
+ pp = p + 2;
+ p = q;
+ }
+ }
+not_ok:
+ ok = false;
+ return QString();
+}
+
+
+
+void Q3DnsAnswer::parseA()
+{
+ if ( next != pp + 4 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN A for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::A;
+ rr->address = QHostAddress( ( answer[pp+0] << 24 ) +
+ ( answer[pp+1] << 16 ) +
+ ( answer[pp+2] << 8 ) +
+ ( answer[pp+3] ) );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseAaaa()
+{
+ if ( next != pp + 16 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Aaaa;
+ rr->address = QHostAddress( answer+pp );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+
+void Q3DnsAnswer::parseMx()
+{
+ if ( next < pp + 2 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN MX for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->priority = (answer[pp] << 8) + answer[pp+1];
+ pp += 2;
+ rr->target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = Q3Dns::Mx;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseSrv()
+{
+ if ( next < pp + 6 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN SRV for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->priority = (answer[pp] << 8) + answer[pp+1];
+ rr->weight = (answer[pp+2] << 8) + answer[pp+3];
+ rr->port = (answer[pp+4] << 8) + answer[pp+5];
+ pp += 6;
+ rr->target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = Q3Dns::Srv;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseCname()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Cname;
+ rr->target = target;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseNs()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ // parse, but ignore
+
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(),
+ target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parsePtr()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Ptr;
+ rr->target = target;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseTxt()
+{
+ QString text = readString(false);
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Txt;
+ rr->text = text;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(),
+ rr->text.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parse()
+{
+ // okay, do the work...
+ if ( (answer[2] & 0x78) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] );
+#endif
+ ok = false;
+ return;
+ }
+
+ // AA
+ bool aa = (answer[2] & 4) != 0;
+
+ // TC
+ if ( (answer[2] & 2) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: truncated answer; pressing on" );
+#endif
+ }
+
+ // RD
+ bool rd = (answer[2] & 1) != 0;
+
+ // we don't test RA
+ // we don't test the MBZ fields
+
+ if ( (answer[3] & 0x0f) == 3 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() );
+#endif
+ // NXDomain. cache that for one minute.
+ rr = new Q3DnsRR( query->l );
+ rr->t = query->t;
+ rr->deleteTime = query->started + 60;
+ rr->expireTime = query->started + 60;
+ rr->nxdomain = true;
+ rr->current = true;
+ rrs->append( rr );
+ return;
+ }
+
+ if ( (answer[3] & 0x0f) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: error code %d", answer[3] & 0x0f );
+#endif
+ ok = false;
+ return;
+ }
+
+ int qdcount = ( answer[4] << 8 ) + answer[5];
+ int ancount = ( answer[6] << 8 ) + answer[7];
+ int nscount = ( answer[8] << 8 ) + answer[9];
+ int adcount = (answer[10] << 8 ) +answer[11];
+
+ pp = 12;
+
+ // read query
+ while( qdcount > 0 && pp < size ) {
+ // should I compare the string against query->l?
+ (void)readString();
+ if ( !ok )
+ return;
+ pp += 4;
+ qdcount--;
+ }
+
+ // answers and stuff
+ int rrno = 0;
+ // if we parse the answer completely, but there are no answers,
+ // ignore the entire thing.
+ int answers = 0;
+ while( ( rrno < ancount ||
+ ( ok && answers >0 && rrno < ancount + nscount + adcount ) ) &&
+ pp < size ) {
+ label = readString().lower();
+ if ( !ok )
+ return;
+ int rdlength = 0;
+ if ( pp + 10 <= size )
+ rdlength = ( answer[pp+8] << 8 ) + answer[pp+9];
+ if ( pp + 10 + rdlength > size ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)",
+ pp, rdlength, size, rrno < ancount );
+#endif
+ // if we're still in the AN section, we should go back and
+ // at least down the TTLs. probably best to invalidate
+ // the results.
+ // the rrs list is good for this
+ ok = ( rrno < ancount );
+ return;
+ }
+ uint type, clas;
+ type = ( answer[pp+0] << 8 ) + answer[pp+1];
+ clas = ( answer[pp+2] << 8 ) + answer[pp+3];
+ ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) +
+ ( answer[pp+6] << 8 ) + answer[pp+7];
+ pp = pp + 10;
+ if ( clas != 1 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: class %d (not internet) for %s",
+ clas, label.isNull() ? "." : label.ascii() );
+#endif
+ } else {
+ next = pp + rdlength;
+ rr = 0;
+ switch( type ) {
+ case 1:
+ parseA();
+ break;
+ case 28:
+ parseAaaa();
+ break;
+ case 15:
+ parseMx();
+ break;
+ case 33:
+ parseSrv();
+ break;
+ case 5:
+ parseCname();
+ break;
+ case 12:
+ parsePtr();
+ break;
+ case 16:
+ parseTxt();
+ break;
+ case 2:
+ parseNs();
+ break;
+ default:
+ // something we don't know
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: type %d for %s", type,
+ label.isNull() ? "." : label.ascii() );
+#endif
+ break;
+ }
+ if ( rr ) {
+ rr->deleteTime = 0;
+ if ( ttl > 0 )
+ rr->expireTime = query->started + ttl;
+ else
+ rr->expireTime = query->started + 20;
+ if ( rrno < ancount ) {
+ answers++;
+ rr->deleteTime = rr->expireTime;
+ }
+ rr->current = true;
+ rrs->append( rr );
+ }
+ }
+ if ( !ok )
+ return;
+ pp = next;
+ next = size;
+ rrno++;
+ }
+ if ( answers == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: answer contained no answers" );
+#endif
+ ok = ( aa && rd );
+ }
+
+ // now go through the list and mark all the As that are referenced
+ // by something we care about. we want to cache such As.
+ rrs->first();
+ Q3Dict<void> used( 17 );
+ used.setAutoDelete( false );
+ while( (rr=rrs->current()) != 0 ) {
+ rrs->next();
+ if ( rr->target.length() && rr->deleteTime > 0 && rr->current )
+ used.insert( rr->target, (void*)42 );
+ if ( ( rr->t == Q3Dns::A || rr->t == Q3Dns::Aaaa ) &&
+ used.find( rr->domain->name() ) != 0 )
+ rr->deleteTime = rr->expireTime;
+ }
+
+ // next, for each RR, delete any older RRs that are equal to it
+ rrs->first();
+ while( (rr=rrs->current()) != 0 ) {
+ rrs->next();
+ if ( rr && rr->domain && rr->domain->rrs ) {
+ Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs;
+ drrs->first();
+ Q3DnsRR * older;
+ while( (older=drrs->current()) != 0 ) {
+ if ( older != rr &&
+ older->t == rr->t &&
+ older->nxdomain == rr->nxdomain &&
+ older->address == rr->address &&
+ older->target == rr->target &&
+ older->priority == rr->priority &&
+ older->weight == rr->weight &&
+ older->port == rr->port &&
+ older->text == rr->text ) {
+ // well, it's equal, but it's not the same. so we kill it,
+ // but use its expiry time.
+#if defined(Q3DNS_DEBUG)
+ qDebug( "killing off old %d for %s, expire was %d",
+ older->t, older->domain->name().latin1(),
+ rr->expireTime );
+#endif
+ older->t = Q3Dns::None;
+ rr->expireTime = QMAX( older->expireTime, rr->expireTime );
+ rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime );
+ older->deleteTime = 0;
+#if defined(Q3DNS_DEBUG)
+ qDebug( " adjusted expire is %d", rr->expireTime );
+#endif
+ }
+ drrs->next();
+ }
+ }
+ }
+
+#if defined(Q3DNS_DEBUG)
+ //qDebug( "DNS Manager: ()" );
+#endif
+}
+
+
+class Q3DnsUgleHack: public Q3Dns {
+public:
+ void ugle( bool emitAnyway=false );
+};
+
+
+void Q3DnsAnswer::notify()
+{
+ if ( !rrs || !ok || !query || !query->dns )
+ return;
+
+ Q3PtrDict<void> notified;
+ notified.setAutoDelete( false );
+
+ Q3PtrDictIterator<void> it( *query->dns );
+ Q3Dns * dns;
+ it.toFirst();
+ while( (dns=(Q3Dns*)(it.current())) != 0 ) {
+ ++it;
+ if ( notified.find( (void*)dns ) == 0 ) {
+ notified.insert( (void*)dns, (void*)42 );
+ if ( rrs->count() == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: found no answers!" );
+#endif
+ dns->d->noNames = true;
+ ((Q3DnsUgleHack*)dns)->ugle( true );
+ } else {
+ QStringList n = dns->qualifiedNames();
+ if ( query && n.contains(query->l) )
+ ((Q3DnsUgleHack*)dns)->ugle();
+#if defined(Q3DNS_DEBUG)
+ else
+ qDebug( "DNS Manager: DNS thing %s not notified for %s",
+ dns->label().ascii(), query->l.ascii() );
+#endif
+ }
+ }
+ }
+}
+
+
+//
+//
+// Q3DnsManager
+//
+//
+
+
+class Q3DnsManager: public Q3DnsSocket {
+private:
+public: // just to silence the moronic g++.
+ Q3DnsManager();
+ ~Q3DnsManager();
+public:
+ static Q3DnsManager * manager();
+
+ Q3DnsDomain * domain( const QString & );
+
+ void transmitQuery( Q3DnsQuery * );
+ void transmitQuery( int );
+
+ // reimplementation of the slots
+ void cleanCache();
+ void retransmit();
+ void answer();
+
+public:
+ Q3PtrVector<Q3DnsQuery> queries;
+ Q3Dict<Q3DnsDomain> cache;
+ Q3SocketDevice * ipv4Socket;
+#if !defined (QT_NO_IPV6)
+ Q3SocketDevice * ipv6Socket;
+#endif
+};
+
+
+
+static Q3DnsManager * globalManager = 0;
+
+static void cleanupDns()
+{
+ delete globalManager;
+ globalManager = 0;
+}
+
+Q3DnsManager * Q3DnsManager::manager()
+{
+ if ( !globalManager ) {
+ qAddPostRoutine(cleanupDns);
+ new Q3DnsManager();
+ }
+ return globalManager;
+}
+
+
+void Q3DnsUgleHack::ugle( bool emitAnyway)
+{
+ if ( emitAnyway || !isWorking() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: status change for %s (type %d)",
+ label().ascii(), recordType() );
+#endif
+ emit resultsReady();
+ }
+}
+
+
+Q3DnsManager::Q3DnsManager()
+ : Q3DnsSocket( qApp, "Internal DNS manager" ),
+ queries( Q3PtrVector<Q3DnsQuery>( 0 ) ),
+ cache( Q3Dict<Q3DnsDomain>( 83, false ) ),
+ ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) )
+#if !defined (QT_NO_IPV6)
+ , ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv6, 0 ) )
+#endif
+{
+ cache.setAutoDelete( true );
+ globalManager = this;
+
+ QTimer * sweepTimer = new QTimer( this );
+ sweepTimer->start( 1000 * 60 * 3 );
+ connect( sweepTimer, SIGNAL(timeout()),
+ this, SLOT(cleanCache()) );
+
+ QSocketNotifier * rn4 = new QSocketNotifier( ipv4Socket->socket(),
+ QSocketNotifier::Read,
+ this, "dns IPv4 socket watcher" );
+ ipv4Socket->setAddressReusable( false );
+ ipv4Socket->setBlocking( false );
+ connect( rn4, SIGNAL(activated(int)), SLOT(answer()) );
+
+#if !defined (QT_NO_IPV6)
+ // Don't connect the IPv6 socket notifier if the host does not
+ // support IPv6.
+ if ( ipv6Socket->socket() != -1 ) {
+ QSocketNotifier * rn6 = new QSocketNotifier( ipv6Socket->socket(),
+ QSocketNotifier::Read,
+ this, "dns IPv6 socket watcher" );
+
+ ipv6support = true;
+ ipv6Socket->setAddressReusable( false );
+ ipv6Socket->setBlocking( false );
+ connect( rn6, SIGNAL(activated(int)), SLOT(answer()) );
+ }
+#endif
+
+ if ( !theNs )
+ Q3Dns::doResInit();
+
+ // O(n*n) stuff here. but for 3 and 6, O(n*n) with a low k should
+ // be perfect. the point is to eliminate any duplicates that
+ // might be hidden in the lists.
+ Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>;
+
+ theNs->first();
+ QHostAddress * h;
+ while( (h=theNs->current()) != 0 ) {
+ ns->first();
+ while( ns->current() != 0 && !(*ns->current() == *h) )
+ ns->next();
+ if ( !ns->current() ) {
+ ns->append( new QHostAddress(*h) );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "using name server %s", h->toString().latin1() );
+ } else {
+ qDebug( "skipping address %s", h->toString().latin1() );
+#endif
+ }
+ theNs->next();
+ }
+
+ delete theNs;
+ theNs = ns;
+ theNs->setAutoDelete( true );
+
+ Q3StrList * domains = new Q3StrList( true );
+
+ theDomains->first();
+ const char * s;
+ while( (s=theDomains->current()) != 0 ) {
+ domains->first();
+ while( domains->current() != 0 && qstrcmp( domains->current(), s ) )
+ domains->next();
+ if ( !domains->current() ) {
+ domains->append( s );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "searching domain %s", s );
+ } else {
+ qDebug( "skipping domain %s", s );
+#endif
+ }
+ theDomains->next();
+ }
+
+ delete theDomains;
+ theDomains = domains;
+ theDomains->setAutoDelete( true );
+}
+
+
+Q3DnsManager::~Q3DnsManager()
+{
+ if ( globalManager )
+ globalManager = 0;
+ queries.setAutoDelete( true );
+ cache.setAutoDelete( true );
+ delete ipv4Socket;
+#if !defined (QT_NO_IPV6)
+ delete ipv6Socket;
+#endif
+}
+
+static Q_UINT32 lastSweep = 0;
+
+void Q3DnsManager::cleanCache()
+{
+ bool again = false;
+ Q3DictIterator<Q3DnsDomain> it( cache );
+ Q3DnsDomain * d;
+ Q_UINT32 thisSweep = now();
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3DnsManager::cleanCache(: Called, time is %u, last was %u",
+ thisSweep, lastSweep );
+#endif
+
+ while( (d=it.current()) != 0 ) {
+ ++it;
+ d->sweep( thisSweep ); // after this, d may be empty
+ if ( !again )
+ again = !d->isEmpty();
+ }
+ if ( !again )
+ delete this;
+ lastSweep = thisSweep;
+}
+
+
+void Q3DnsManager::retransmit()
+{
+ const QObject * o = sender();
+ if ( o == 0 || globalManager == 0 || this != globalManager )
+ return;
+ uint q = 0;
+ while( q < queries.size() && queries[q] != o )
+ q++;
+ if ( q < queries.size() )
+ transmitQuery( q );
+}
+
+
+void Q3DnsManager::answer()
+{
+ QByteArray a( 16383 ); // large enough for anything, one suspects
+
+ int r;
+#if defined (QT_NO_IPV6)
+ r = ipv4Socket->readBlock(a.data(), a.size());
+#else
+ if (((QSocketNotifier *)sender())->socket() == ipv4Socket->socket())
+ r = ipv4Socket->readBlock(a.data(), a.size());
+ else
+ r = ipv6Socket->readBlock(a.data(), a.size());
+#endif
+#if defined(Q3DNS_DEBUG)
+#if !defined (QT_NO_IPV6)
+ qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
+ useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii()
+ : ipv6Socket->peerAddress().toString().ascii(),
+ useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() );
+#else
+ qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
+ ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());;
+#endif
+#endif
+ if ( r < 12 )
+ return;
+ // maybe we should check that the answer comes from port 53 on one
+ // of our name servers...
+ a.resize( r );
+
+ Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]);
+ uint i = 0;
+ while( i < queries.size() &&
+ !( queries[i] && queries[i]->id == aid ) )
+ i++;
+ if ( i == queries.size() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i );
+#endif
+ return;
+ }
+
+ // at this point queries[i] is whatever we asked for.
+
+ if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: received a query" );
+#endif
+ return;
+ }
+
+ Q3DnsQuery * q = queries[i];
+ Q3DnsAnswer answer( a, q );
+ answer.parse();
+ if ( answer.ok ) {
+ queries.take( i );
+ answer.notify();
+ delete q;
+ }
+}
+
+
+void Q3DnsManager::transmitQuery( Q3DnsQuery * query_ )
+{
+ if ( !query_ )
+ return;
+
+ uint i = 0;
+ while( i < queries.size() && queries[i] != 0 )
+ i++;
+ if ( i == queries.size() )
+ queries.resize( i+1 );
+ queries.insert( i, query_ );
+ transmitQuery( i );
+}
+
+
+void Q3DnsManager::transmitQuery( int i )
+{
+ if ( i < 0 || i >= (int)queries.size() )
+ return;
+ Q3DnsQuery * q = queries[i];
+
+ if ( q && q->step > 8 ) {
+ // okay, we've run out of retransmissions. we fake an NXDomain
+ // with a very short life time...
+ Q3DnsAnswer answer( q );
+ answer.notify();
+ // and then get rid of the query
+ queries.take( i );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: giving up on query 0x%04x", q->id );
+#endif
+ delete q;
+ QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ if ((q && !q->dns) || q->dns->isEmpty())
+ // no one currently wants the answer, so there's no point in
+ // retransmitting the query. we keep it, though. an answer may
+ // arrive for an earlier query transmission, and if it does we
+ // may benefit from caching the result.
+ return;
+
+ QByteArray p( 12 + q->l.length() + 2 + 4 );
+ if ( p.size() > 500 )
+ return; // way over the limit, so don't even try
+
+ // header
+ // id
+ p[0] = (q->id & 0xff00) >> 8;
+ p[1] = q->id & 0x00ff;
+ p[2] = 1; // recursion desired, rest is 0
+ p[3] = 0; // all is 0
+ // one query
+ p[4] = 0;
+ p[5] = 1;
+ // no answers, name servers or additional data
+ p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
+
+ // the name is composed of several components. each needs to be
+ // written by itself... so we write...
+ // oh, and we assume that there's no funky characters in there.
+ int pp = 12;
+ uint lp = 0;
+ while( lp < (uint) q->l.length() ) {
+ int le = q->l.find( QLatin1Char('.'), lp );
+ if ( le < 0 )
+ le = q->l.length();
+ QString component = q->l.mid( lp, le-lp );
+ p[pp++] = component.length();
+ int cp;
+ for( cp=0; cp < (int)component.length(); cp++ )
+ p[pp++] = component[cp].latin1();
+ lp = le + 1;
+ }
+ // final null
+ p[pp++] = 0;
+ // query type
+ p[pp++] = 0;
+ switch( q->t ) {
+ case Q3Dns::A:
+ p[pp++] = 1;
+ break;
+ case Q3Dns::Aaaa:
+ p[pp++] = 28;
+ break;
+ case Q3Dns::Mx:
+ p[pp++] = 15;
+ break;
+ case Q3Dns::Srv:
+ p[pp++] = 33;
+ break;
+ case Q3Dns::Cname:
+ p[pp++] = 5;
+ break;
+ case Q3Dns::Ptr:
+ p[pp++] = 12;
+ break;
+ case Q3Dns::Txt:
+ p[pp++] = 16;
+ break;
+ default:
+ p[pp++] = (char)255; // any
+ break;
+ }
+ // query class (always internet)
+ p[pp++] = 0;
+ p[pp++] = 1;
+
+ // if we have no name servers, we should regenerate ns in case
+ // name servers have recently been defined (like on windows,
+ // plugging/unplugging the network cable will change the name
+ // server entries)
+ if ( !theNs || theNs->isEmpty() )
+ Q3Dns::doResInit();
+
+ if ( !theNs || theNs->isEmpty() ) {
+ // we don't find any name servers. We fake an NXDomain
+ // with a very short life time...
+ Q3DnsAnswer answer( q );
+ answer.notify();
+ // and then get rid of the query
+ queries.take( i );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id );
+#endif
+ delete q;
+ QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ QHostAddress receiver = *theNs->at( q->step % theNs->count() );
+ if (receiver.isIPv4Address())
+ ipv4Socket->writeBlock( p.data(), pp, receiver, 53 );
+#if !defined (QT_NO_IPV6)
+ else
+ ipv6Socket->writeBlock( p.data(), pp, receiver, 53 );
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "issuing query 0x%04x (%d) about %s type %d to %s",
+ q->id, q->step, q->l.ascii(), q->t,
+ ns->at( q->step % ns->count() )->toString().ascii() );
+#endif
+ if ( theNs->count() > 1 && q->step == 0 && queries.count() == 1 ) {
+ // if it's the first time, and we don't have any other
+ // outstanding queries, send nonrecursive queries to the other
+ // name servers too.
+ p[2] = 0;
+ QHostAddress * server;
+ while( (server=theNs->next()) != 0 ) {
+ if (server->isIPv4Address())
+ ipv4Socket->writeBlock( p.data(), pp, *server, 53 );
+#if !defined (QT_NO_IPV6)
+ else
+ ipv6Socket->writeBlock( p.data(), pp, *server, 53 );
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "copying query to %s", server->toString().ascii() );
+#endif
+ }
+ }
+ q->step++;
+ // some testing indicates that normal dns queries take up to 0.6
+ // seconds. the graph becomes steep around that point, and the
+ // number of errors rises... so it seems good to retry at that
+ // point.
+ q->start( q->step < theNs->count() ? 800 : 1500, true );
+}
+
+
+Q3DnsDomain * Q3DnsManager::domain( const QString & label )
+{
+ Q3DnsDomain * d = cache.find( label );
+ if ( !d ) {
+ d = new Q3DnsDomain( label );
+ cache.insert( label, d );
+ }
+ return d;
+}
+
+
+//
+//
+// the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for
+// each domain, and the cached Q3DnsRRs. (A domain, in DNS terminology, is
+// a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are
+// all domains.)
+//
+//
+
+
+Q3DnsDomain::Q3DnsDomain( const QString & label )
+{
+ l = label;
+ rrs = 0;
+}
+
+
+Q3DnsDomain::~Q3DnsDomain()
+{
+ delete rrs;
+ rrs = 0;
+}
+
+
+void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr )
+{
+ Q3DnsDomain * d = Q3DnsManager::manager()->domain( label );
+ if ( !d->rrs ) {
+ d->rrs = new Q3PtrList<Q3DnsRR>;
+ d->rrs->setAutoDelete( true );
+ }
+ d->rrs->append( rr );
+ rr->domain = d;
+}
+
+
+Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r )
+{
+ Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>;
+
+ // test at first if you have to start a query at all
+ if ( r->recordType() == Q3Dns::A ) {
+ if ( r->label().lower() == QLatin1String("localhost") ) {
+ // undocumented hack. ipv4-specific. also, may be a memory
+ // leak? not sure. would be better to do this in doResInit(),
+ // anyway.
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ rrTmp->t = Q3Dns::A;
+ rrTmp->address = QHostAddress( 0x7f000001 );
+ rrTmp->current = true;
+ l->append( rrTmp );
+ return l;
+ }
+ QHostAddress tmp;
+ if ( tmp.setAddress( r->label() ) ) {
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ if ( tmp.isIPv4Address() ) {
+ rrTmp->t = Q3Dns::A;
+ rrTmp->address = tmp;
+ rrTmp->current = true;
+ l->append( rrTmp );
+ } else {
+ rrTmp->nxdomain = true;
+ }
+ return l;
+ }
+ }
+ if ( r->recordType() == Q3Dns::Aaaa ) {
+ QHostAddress tmp;
+ if ( tmp.setAddress(r->label()) ) {
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ if ( tmp.isIPv6Address() ) {
+ rrTmp->t = Q3Dns::Aaaa;
+ rrTmp->address = tmp;
+ rrTmp->current = true;
+ l->append( rrTmp );
+ } else {
+ rrTmp->nxdomain = true;
+ }
+ return l;
+ }
+ }
+
+ // if you reach this point, you have to do the query
+ Q3DnsManager * m = Q3DnsManager::manager();
+ QStringList n = r->qualifiedNames();
+ bool nxdomain;
+ int cnamecount = 0;
+ int it = 0;
+ while( it < n.count() ) {
+ QString s = n.at(it++);
+ nxdomain = false;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "looking at cache for %s (%s %d)",
+ s.ascii(), r->label().ascii(), r->recordType() );
+#endif
+ Q3DnsDomain * d = m->domain( s );
+#if defined(Q3DNS_DEBUG)
+ qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 );
+#endif
+ if ( d->rrs )
+ d->rrs->first();
+ Q3DnsRR * rr;
+ bool answer = false;
+ while( d->rrs && (rr=d->rrs->current()) != 0 ) {
+ if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname &&
+ !rr->nxdomain && cnamecount < 16 ) {
+ // cname. if the code is ugly, that may just
+ // possibly be because the concept is.
+#if defined(Q3DNS_DEBUG)
+ qDebug( "found cname from %s to %s",
+ r->label().ascii(), rr->target.ascii() );
+#endif
+ s = rr->target;
+ d = m->domain( s );
+ if ( d->rrs )
+ d->rrs->first();
+ it = n.count();
+ // we've elegantly moved over to whatever the cname
+ // pointed to. well, not elegantly. let's remember
+ // that we've done something, anyway, so we can't be
+ // fooled into an infinte loop as well.
+ cnamecount++;
+ } else {
+ if ( rr->t == r->recordType() ) {
+ if ( rr->nxdomain )
+ nxdomain = true;
+ else
+ answer = true;
+ l->append( rr );
+ if ( rr->deleteTime <= lastSweep ) {
+ // we're returning something that'll be
+ // deleted soon. we assume that if the client
+ // wanted it twice, it'll want it again, so we
+ // ask the name server again right now.
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = rr->t;
+ query->l = rr->domain->name();
+ // note that here, we don't bother about
+ // notification. but we do bother about
+ // timeouts: we make sure to use high timeouts
+ // and few tramsissions.
+ query->step = theNs->count();
+ QObject::connect( query, SIGNAL(timeout()),
+ Q3DnsManager::manager(),
+ SLOT(retransmit()) );
+ Q3DnsManager::manager()->transmitQuery( query );
+ }
+ }
+ d->rrs->next();
+ }
+ }
+ // if we found a positive result, return quickly
+ if ( answer && l->count() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "found %d records for %s",
+ l->count(), r->label().ascii() );
+ l->first();
+ while( l->current() ) {
+ qDebug( " type %d target %s address %s",
+ l->current()->t,
+ l->current()->target.latin1(),
+ l->current()->address.toString().latin1() );
+ l->next();
+ }
+#endif
+ l->first();
+ return l;
+ }
+
+#if defined(Q3DNS_DEBUG)
+ if ( nxdomain )
+ qDebug( "found NXDomain %s", s.ascii() );
+#endif
+
+ if ( !nxdomain ) {
+ // if we didn't, and not a negative result either, perhaps
+ // we need to transmit a query.
+ uint q = 0;
+ while ( q < m->queries.size() &&
+ ( m->queries[q] == 0 ||
+ m->queries[q]->t != r->recordType() ||
+ m->queries[q]->l != s ) )
+ q++;
+ // we haven't done it before, so maybe we should. but
+ // wait - if it's an unqualified name, only ask when all
+ // the other alternatives are exhausted.
+ if ( q == m->queries.size() && ( s.find( QLatin1Char('.') ) >= 0 ||
+ int(l->count()) >= n.count()-1 ) ) {
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = r->recordType();
+ query->l = s;
+ query->dns->replace( (void*)r, (void*)r );
+ QObject::connect( query, SIGNAL(timeout()),
+ Q3DnsManager::manager(), SLOT(retransmit()) );
+ Q3DnsManager::manager()->transmitQuery( query );
+ } else if ( q < m->queries.size() ) {
+ // if we've found an earlier query for the same
+ // domain/type, subscribe to its answer
+ m->queries[q]->dns->replace( (void*)r, (void*)r );
+ }
+ }
+ }
+ l->first();
+ return l;
+}
+
+
+void Q3DnsDomain::sweep( Q_UINT32 thisSweep )
+{
+ if ( !rrs )
+ return;
+
+ Q3DnsRR * rr;
+ rrs->first();
+ while( (rr=rrs->current()) != 0 ) {
+ if ( !rr->deleteTime )
+ rr->deleteTime = thisSweep; // will hit next time around
+
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s",
+ rr->domain->name().latin1(), rr->t,
+ rr->expireTime, rr->deleteTime,
+ rr->target.latin1(), rr->address.toString().latin1());
+#endif
+ if ( rr->current == false ||
+ rr->t == Q3Dns::None ||
+ rr->deleteTime <= thisSweep ||
+ rr->expireTime <= thisSweep )
+ rrs->remove();
+ else
+ rrs->next();
+ }
+
+ if ( rrs->isEmpty() ) {
+ delete rrs;
+ rrs = 0;
+ }
+}
+
+
+
+
+// the itsy-bitsy little socket class I don't really need except for
+// so I can subclass and reimplement the slots.
+
+
+Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name )
+ : QObject( parent, name )
+{
+ // nothing
+}
+
+
+Q3DnsSocket::~Q3DnsSocket()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::cleanCache()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::retransmit()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::answer()
+{
+ // nothing
+}
+
+
+/*!
+ \class Q3Dns
+ \brief The Q3Dns class provides asynchronous DNS lookups.
+
+ \compat
+
+ Both Windows and Unix provide synchronous DNS lookups; Windows
+ provides some asynchronous support too. At the time of writing
+ neither operating system provides asynchronous support for
+ anything other than hostname-to-address mapping.
+
+ Q3Dns rectifies this shortcoming, by providing asynchronous caching
+ lookups for the record types that we expect modern GUI
+ applications to need in the near future.
+
+ The class is \e not straightforward to use (although it is much
+ simpler than the native APIs); Q3Socket provides much easier to use
+ TCP connection facilities. The aim of Q3Dns is to provide a correct
+ and small API to the DNS and nothing more. (We use "correctness"
+ to mean that the DNS information is correctly cached, and
+ correctly timed out.)
+
+ The API comprises a constructor, functions to set the DNS node
+ (the domain in DNS terminology) and record type (setLabel() and
+ setRecordType()), the corresponding get functions, an isWorking()
+ function to determine whether Q3Dns is working or reading, a
+ resultsReady() signal and query functions for the result.
+
+ There is one query function for each RecordType, namely
+ addresses(), mailServers(), servers(), hostNames() and texts().
+ There are also two generic query functions: canonicalName()
+ returns the name you'll presumably end up using (the exact meaning
+ of this depends on the record type) and qualifiedNames() returns a
+ list of the fully qualified names label() maps to.
+
+ \sa Q3Socket
+*/
+
+/*!
+ Constructs a DNS query object with invalid settings for both the
+ label and the search type.
+*/
+
+Q3Dns::Q3Dns()
+{
+ d = new Q3DnsPrivate;
+ t = None;
+}
+
+
+
+
+/*!
+ Constructs a DNS query object that will return record type \a rr
+ information about \a label.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \a rr defaults to \c A, IPv4 addresses.
+*/
+
+Q3Dns::Q3Dns( const QString & label, RecordType rr )
+{
+ d = new Q3DnsPrivate;
+ t = rr;
+ setLabel( label );
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+
+
+/*!
+ Constructs a DNS query object that will return record type \a rr
+ information about host address \a address. The label is set to the
+ IN-ADDR.ARPA domain name. This is useful in combination with the
+ \c Ptr record type (e.g. if you want to look up a hostname for a
+ given address).
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \a rr defaults to \c Ptr, that maps addresses to hostnames.
+*/
+
+Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr )
+{
+ d = new Q3DnsPrivate;
+ t = rr;
+ setLabel( address );
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+
+
+
+/*!
+ Destroys the DNS query object and frees its allocated resources.
+*/
+
+Q3Dns::~Q3Dns()
+{
+ if ( globalManager ) {
+ uint q = 0;
+ Q3DnsManager * m = globalManager;
+ while( q < m->queries.size() ) {
+ Q3DnsQuery * query=m->queries[q];
+ if ( query && query->dns )
+ (void)query->dns->take( (void*) this );
+ q++;
+ }
+
+ }
+
+ delete d;
+ d = 0;
+}
+
+
+
+
+/*!
+ Sets this DNS query object to query for information about \a
+ label.
+
+ This does not change the recordType(), but its isWorking() status
+ will probably change as a result.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+*/
+
+void Q3Dns::setLabel( const QString & label )
+{
+ l = label;
+ d->noNames = false;
+
+ // construct a list of qualified names
+ n.clear();
+ if ( l.length() > 1 && l[(int)l.length()-1] == QLatin1Char('.') ) {
+ n.append( l.left( l.length()-1 ).lower() );
+ } else {
+ int i = l.length();
+ int dots = 0;
+ const int maxDots = 2;
+ while( i && dots < maxDots ) {
+ if ( l[--i] == QLatin1Char('.') )
+ dots++;
+ }
+ if ( dots < maxDots ) {
+ (void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there
+ Q3StrListIterator it( *theDomains );
+ const char * dom;
+ while( (dom=it.current()) != 0 ) {
+ ++it;
+ n.append( l.lower() + QLatin1Char('.') + QLatin1String(dom) );
+ }
+ }
+ n.append( l.lower() );
+ }
+
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( d->noEventLoop ) {
+ doSynchronousLookup();
+ } else {
+ setStartQueryTimer(); // start query the next time we enter event loop
+ }
+#else
+ setStartQueryTimer(); // start query the next time we enter event loop
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() );
+ int i = 0;
+ for( i = 0; i < (int)n.count(); i++ )
+ qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() );
+#endif
+}
+
+
+/*!
+ \overload
+
+ Sets this DNS query object to query for information about the host
+ address \a address. The label is set to the IN-ADDR.ARPA domain
+ name. This is useful in combination with the \c Ptr record type
+ (e.g. if you want to look up a hostname for a given address).
+*/
+
+void Q3Dns::setLabel( const QHostAddress & address )
+{
+ setLabel( toInAddrArpaDomain( address ) );
+}
+
+
+/*!
+ \fn QStringList Q3Dns::qualifiedNames() const
+
+ Returns a list of the fully qualified names label() maps to.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 0
+
+*/
+
+
+/*!
+ \fn QString Q3Dns::label() const
+
+ Returns the domain name for which this object returns information.
+
+ \sa setLabel()
+*/
+
+/*!
+ \enum Q3Dns::RecordType
+
+ This enum type defines the record types Q3Dns can handle. The DNS
+ provides many more; these are the ones we've judged to be in
+ current use, useful for GUI programs and important enough to
+ support right away:
+
+ \value None No information. This exists only so that Q3Dns can
+ have a default.
+
+ \value A IPv4 addresses. By far the most common type.
+
+ \value Aaaa IPv6 addresses. So far mostly unused.
+
+ \value Mx Mail eXchanger names. Used for mail delivery.
+
+ \value Srv SeRVer names. Generic record type for finding
+ servers. So far mostly unused.
+
+ \value Cname Canonical names. Maps from nicknames to the true
+ name (the canonical name) for a host.
+
+ \value Ptr name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames.
+
+ \value Txt arbitrary TeXT for domains.
+
+ We expect that some support for the
+ \l{http://www.rfc-editor.org/rfc/rfc2535.txt}{RFC 2535}
+ extensions will be added in future versions.
+*/
+
+/*!
+ Sets this object to query for record type \a rr records.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \sa RecordType
+*/
+
+void Q3Dns::setRecordType( RecordType rr )
+{
+ t = rr;
+ d->noNames = false;
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+/*!
+ \internal
+
+ Private slot for starting the query.
+*/
+void Q3Dns::startQuery()
+{
+ // isWorking() starts the query (if necessary)
+ if ( !isWorking() )
+ emit resultsReady();
+}
+
+/*!
+ The three functions Q3Dns::Q3Dns(QString, RecordType),
+ Q3Dns::setLabel() and Q3Dns::setRecordType() may start a DNS lookup.
+ This function handles setting up the single shot timer.
+*/
+void Q3Dns::setStartQueryTimer()
+{
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( !d->queryTimer && !d->noEventLoop )
+#else
+ if ( !d->queryTimer )
+#endif
+ {
+ // start the query the next time we enter event loop
+ d->queryTimer = new QTimer( this );
+ connect( d->queryTimer, SIGNAL(timeout()),
+ this, SLOT(startQuery()) );
+ d->queryTimer->start( 0, true );
+ }
+}
+
+/*
+ Transforms the host address \a address to the IN-ADDR.ARPA domain
+ name. Returns something indeterminate if you're sloppy or
+ naughty. This function has an IPv4-specific name, but works for
+ IPv6 too.
+*/
+QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address )
+{
+ QString s;
+ if ( address.isNull() ) {
+ // if the address isn't valid, neither of the other two make
+ // cases make sense. better to just return.
+ } else if ( address.isIp4Addr() ) {
+ Q_UINT32 i = address.ip4Addr();
+ s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA",
+ i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff );
+ } else {
+ // RFC 3152. (1886 is deprecated, and clients no longer need to
+ // support it, in practice).
+ Q_IPV6ADDR i = address.toIPv6Address();
+ s = QLatin1String("ip6.arpa");
+ uint b = 0;
+ while( b < 16 ) {
+ s = QString::number( i.c[b]%16, 16 ) + QLatin1Char('.') +
+ QString::number( i.c[b]/16, 16 ) + QLatin1Char('.') + s;
+ b++;
+ }
+ }
+ return s;
+}
+
+
+/*!
+ \fn Q3Dns::RecordType Q3Dns::recordType() const
+
+ Returns the record type of this DNS query object.
+
+ \sa setRecordType() RecordType
+*/
+
+/*!
+ \fn void Q3Dns::resultsReady()
+
+ This signal is emitted when results are available for one of the
+ qualifiedNames().
+*/
+
+/*!
+ Returns true if Q3Dns is doing a lookup for this object (i.e. if it
+ does not already have the necessary information); otherwise
+ returns false.
+
+ Q3Dns emits the resultsReady() signal when the status changes to false.
+*/
+
+bool Q3Dns::isWorking() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t );
+#endif
+ if ( t == None )
+ return false;
+
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( d->noEventLoop )
+ return true;
+#endif
+
+ Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this );
+ Q_LONG queries = n.count();
+ while( ll->current() != 0 ) {
+ if ( ll->current()->nxdomain ) {
+ queries--;
+ } else {
+ delete ll;
+ return false;
+ }
+ ll->next();
+ }
+ delete ll;
+
+ if ( queries <= 0 )
+ return false;
+ if ( d->noNames )
+ return false;
+ return true;
+}
+
+
+/*!
+ Returns a list of the addresses for this name if this Q3Dns object
+ has a recordType() of Q3Dns::A or Q3Dns::Aaaa and the answer
+ is available; otherwise returns an empty list.
+
+ As a special case, if label() is a valid numeric IP address, this
+ function returns that address.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 1
+
+*/
+
+Q3ValueList<QHostAddress> Q3Dns::addresses() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::addresses (%s)", l.ascii() );
+#endif
+ Q3ValueList<QHostAddress> result;
+ if ( t != A && t != Aaaa )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain )
+ result.append( rr->address );
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ \class Q3Dns::MailServer
+ \brief The Q3Dns::MailServer class is described in Q3Dns::mailServers().
+
+*/
+
+
+/*! \fn Q3Dns::MailServer::MailServer(const QString& n, Q_UINT16 p)
+ The constructor sets the public data members name and priority.
+ \a n is the name and \a p is the priority.
+*/
+
+/*!
+ Returns a list of mail servers if the record type is \c Mx. The
+ class Q3Dns::MailServer contains the following public variables:
+ \list
+ \o QString Q3Dns::MailServer::name
+ \o Q_UINT16 Q3Dns::MailServer::priority
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 2
+
+*/
+Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::mailServers (%s)", l.ascii() );
+#endif
+ Q3ValueList<Q3Dns::MailServer> result;
+ if ( t != Mx )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ MailServer ms( rr->target, rr->priority );
+ result.append( ms );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+/*!
+ \class Q3Dns::Server
+ \brief The Q3Dns::Server class is described in Q3Dns::servers().
+
+*/
+
+/*! \fn Q3Dns::Server::Server(const QString& n, Q_UINT16 p, Q_UINT16 w, Q_UINT16 po)
+ The constructor sets the public data members name, priority,
+ weight, and port. \a n is the name, \a p is the priority,
+ \a w is the weight, and \a po is the port.
+*/
+
+/*!
+ Returns a list of servers if the record type is \c Srv. The class
+ Q3Dns::Server contains the following public variables:
+ \list
+ \o QString Q3Dns::Server::name
+ \o Q_UINT16 Q3Dns::Server::priority
+ \o Q_UINT16 Q3Dns::Server::weight
+ \o Q_UINT16 Q3Dns::Server::port
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 3
+*/
+Q3ValueList<Q3Dns::Server> Q3Dns::servers() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::servers (%s)", l.ascii() );
+#endif
+ Q3ValueList<Q3Dns::Server> result;
+ if ( t != Srv )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ Server s( rr->target, rr->priority, rr->weight, rr->port );
+ result.append( s );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns a list of host names if the record type is \c Ptr.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 4
+
+*/
+QStringList Q3Dns::hostNames() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::hostNames (%s)", l.ascii() );
+#endif
+ QStringList result;
+ if ( t != Ptr )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ QString str( rr->target );
+ result.append( str );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns a list of texts if the record type is \c Txt.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 5
+*/
+QStringList Q3Dns::texts() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::texts (%s)", l.ascii() );
+#endif
+ QStringList result;
+ if ( t != Txt )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ QString str( rr->text );
+ result.append( str );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns the canonical name for this DNS node. (This works
+ regardless of what recordType() is set to.)
+
+ If the canonical name isn't known, this function returns a null
+ string.
+
+ The canonical name of a DNS node is its full name, or the full
+ name of the target of its CNAME. For example, if l.trolltech.com
+ is a CNAME to lillian.troll.no, and the search path for Q3Dns is
+ "trolltech.com", then the canonical name for all of "lillian",
+ "l", "lillian.troll.no." and "l.trolltech.com" is
+ "lillian.troll.no.".
+*/
+
+QString Q3Dns::canonicalName() const
+{
+ // the cname should work regardless of the recordType(), so set the record
+ // type temporarily to cname when you look at the cache
+ Q3Dns *that = (Q3Dns*) this; // mutable function
+ RecordType oldType = t;
+ that->t = Cname;
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that );
+ that->t = oldType;
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain && rr->domain ) {
+ delete cached;
+ return rr->target;
+ }
+ cached->next();
+ }
+ delete cached;
+ return QString();
+}
+
+#if defined(Q_DNS_SYNCHRONOUS)
+/*! \reimp
+*/
+void Q3Dns::connectNotify( const char *signal )
+{
+ if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) {
+ doSynchronousLookup();
+ }
+}
+#endif
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN)
+
+#if defined(Q_DNS_SYNCHRONOUS)
+void Q3Dns::doSynchronousLookup()
+{
+ // ### not implemented yet
+}
+#endif
+
+// the following typedefs are needed for GetNetworkParams() API call
+#ifndef IP_TYPES_INCLUDED
+#define MAX_HOSTNAME_LEN 128
+#define MAX_DOMAIN_NAME_LEN 128
+#define MAX_SCOPE_ID_LEN 256
+typedef struct {
+ char String[4 * 4];
+} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
+typedef struct _IP_ADDR_STRING {
+ struct _IP_ADDR_STRING* Next;
+ IP_ADDRESS_STRING IpAddress;
+ IP_MASK_STRING IpMask;
+ DWORD Context;
+} IP_ADDR_STRING, *PIP_ADDR_STRING;
+typedef struct {
+ char HostName[MAX_HOSTNAME_LEN + 4] ;
+ char DomainName[MAX_DOMAIN_NAME_LEN + 4];
+ PIP_ADDR_STRING CurrentDnsServer;
+ IP_ADDR_STRING DnsServerList;
+ UINT NodeType;
+ char ScopeId[MAX_SCOPE_ID_LEN + 4];
+ UINT EnableRouting;
+ UINT EnableProxy;
+ UINT EnableDns;
+} FIXED_INFO, *PFIXED_INFO;
+#endif
+typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG );
+
+// ### FIXME: this code is duplicated in qfiledialog.cpp
+static QString getWindowsRegString(HKEY key, const QString &subKey)
+{
+ QString s;
+
+ wchar_t buf[1024];
+ DWORD bsz = sizeof(buf) / sizeof(wchar_t);
+ int r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)buf, &bsz);
+ if (r == ERROR_SUCCESS) {
+ s = QString::fromWCharArray(buf);
+ } else if (r == ERROR_MORE_DATA) {
+ char *ptr = new char[bsz+1];
+ r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)ptr, &bsz);
+ if (r == ERROR_SUCCESS)
+ s = QLatin1String(ptr);
+ delete [] ptr;
+ }
+
+ return s;
+}
+
+static bool getDnsParamsFromRegistry( const QString &path,
+ QString *domainName, QString *nameServer, QString *searchList )
+{
+ HKEY k;
+ int r = RegOpenKeyEx( HKEY_LOCAL_MACHINE, (wchar_t*)path.utf16(), 0, KEY_READ, &k );
+
+ if ( r == ERROR_SUCCESS ) {
+ *domainName = getWindowsRegString( k, QLatin1String("DhcpDomain") );
+ if ( domainName->isEmpty() )
+ *domainName = getWindowsRegString( k, QLatin1String("Domain") );
+
+ *nameServer = getWindowsRegString( k, QLatin1String("DhcpNameServer") );
+ if ( nameServer->isEmpty() )
+ *nameServer = getWindowsRegString( k, QLatin1String("NameServer") );
+
+ *searchList = getWindowsRegString( k, QLatin1String("SearchList") );
+ }
+ RegCloseKey( k );
+ return r == ERROR_SUCCESS;
+}
+
+void Q3Dns::doResInit()
+{
+ char separator = 0;
+
+ if ( theNs )
+ delete theNs;
+ theNs = new Q3PtrList<QHostAddress>;
+ theNs->setAutoDelete( true );
+ theDomains = new Q3StrList( true );
+ theDomains->setAutoDelete( true );
+
+ QString domainName, nameServer, searchList;
+
+ bool gotNetworkParams = false;
+ // try the API call GetNetworkParams() first and use registry lookup only
+ // as a fallback
+ HINSTANCE hinstLib = QSystemLibrary::load( L"iphlpapi" );
+ if ( hinstLib != 0 ) {
+#ifdef Q_OS_WINCE
+ GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, L"GetNetworkParams" );
+#else
+ GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" );
+#endif
+ if ( getNetworkParams != 0 ) {
+ ULONG l = 0;
+ DWORD res;
+ res = getNetworkParams( 0, &l );
+ if ( res == ERROR_BUFFER_OVERFLOW ) {
+ FIXED_INFO *finfo = (FIXED_INFO*)new char[l];
+ res = getNetworkParams( finfo, &l );
+ if ( res == ERROR_SUCCESS ) {
+ domainName = QLatin1String(finfo->DomainName);
+ nameServer = QLatin1String("");
+ IP_ADDR_STRING *dnsServer = &finfo->DnsServerList;
+ while ( dnsServer != 0 ) {
+ nameServer += QLatin1String(dnsServer->IpAddress.String);
+ dnsServer = dnsServer->Next;
+ if ( dnsServer != 0 )
+ nameServer += QLatin1Char(' ');
+ }
+ searchList = QLatin1String("");
+ separator = ' ';
+ gotNetworkParams = true;
+ }
+ delete[] finfo;
+ }
+ }
+ FreeLibrary( hinstLib );
+ }
+ if ( !gotNetworkParams ) {
+ if ( getDnsParamsFromRegistry(
+ QLatin1String("System\\CurrentControlSet\\Services\\Tcpip\\Parameters"),
+ &domainName, &nameServer, &searchList )) {
+ separator = ' ';
+ } else {
+ // Could not access the TCP/IP parameters
+ domainName = QLatin1String("");
+ nameServer = QLatin1String("127.0.0.1");
+ searchList = QLatin1String("");
+ separator = ' ';
+ }
+ }
+
+ nameServer = nameServer.simplifyWhiteSpace();
+ int first, last;
+ if ( !nameServer.isEmpty() ) {
+ first = 0;
+ do {
+ last = nameServer.find( QLatin1Char(separator), first );
+ if ( last < 0 )
+ last = nameServer.length();
+ Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A );
+ Q3ValueList<QHostAddress> address = tmp.addresses();
+ Q_LONG i = address.count();
+ while( i )
+ theNs->append( new QHostAddress(address[--i]) );
+ first = last+1;
+ } while( first < (int)nameServer.length() );
+ }
+
+ searchList += QLatin1Char(' ') + domainName;
+ searchList = searchList.simplifyWhiteSpace().lower();
+ first = 0;
+ do {
+ last = searchList.find( QLatin1Char(separator), first );
+ if ( last < 0 )
+ last = searchList.length();
+ theDomains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) );
+ first = last+1;
+ } while( first < (int)searchList.length() );
+}
+
+#elif defined(Q_OS_UNIX)
+
+#if defined(Q_DNS_SYNCHRONOUS)
+void Q3Dns::doSynchronousLookup()
+{
+ if ( t!=None && !l.isEmpty() ) {
+ Q3ValueListIterator<QString> it = n.begin();
+ Q3ValueListIterator<QString> end = n.end();
+ int type;
+ switch( t ) {
+ case Q3Dns::A:
+ type = 1;
+ break;
+ case Q3Dns::Aaaa:
+ type = 28;
+ break;
+ case Q3Dns::Mx:
+ type = 15;
+ break;
+ case Q3Dns::Srv:
+ type = 33;
+ break;
+ case Q3Dns::Cname:
+ type = 5;
+ break;
+ case Q3Dns::Ptr:
+ type = 12;
+ break;
+ case Q3Dns::Txt:
+ type = 16;
+ break;
+ default:
+ type = (char)255; // any
+ break;
+ }
+ while( it != end ) {
+ QString s = *it;
+ it++;
+ QByteArray ba( 512 );
+ int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() );
+ if ( len > 0 ) {
+ ba.resize( len );
+
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = t;
+ query->l = s;
+ Q3DnsAnswer a( ba, query );
+ a.parse();
+ } else if ( len == -1 ) {
+ // res_search error
+ }
+ }
+ emit resultsReady();
+ }
+}
+#endif
+
+#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
+#define Q_MODERN_RES_API
+#endif
+
+void Q3Dns::doResInit()
+{
+ if ( theNs )
+ return;
+ theNs = new Q3PtrList<QHostAddress>;
+ theNs->setAutoDelete( true );
+ theDomains = new Q3StrList( true );
+ theDomains->setAutoDelete( true );
+
+ // read resolv.conf manually.
+ QFile resolvConf(QLatin1String("/etc/resolv.conf"));
+ if (resolvConf.open(QIODevice::ReadOnly)) {
+ QTextStream stream( &resolvConf );
+ QString line;
+
+ while ( !stream.atEnd() ) {
+ line = stream.readLine();
+ QStringList list = QStringList::split( QLatin1String(" "), line );
+ if( line.startsWith( QLatin1Char('#') ) || list.size() < 2 )
+ continue;
+ const QString type = list[0].lower();
+
+ if ( type == QLatin1String("nameserver") ) {
+ QHostAddress *address = new QHostAddress();
+ if ( address->setAddress( QString(list[1]) ) ) {
+ // only add ipv6 addresses from resolv.conf if
+ // this host supports ipv6.
+ if ( address->isIPv4Address() || ipv6support )
+ theNs->append( address );
+ else
+ delete address;
+ } else {
+ delete address;
+ }
+ } else if ( type == QLatin1String("search") ) {
+ QStringList srch = QStringList::split( QLatin1String(" "), list[1] );
+ for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i )
+ theDomains->append( (*i).lower().local8Bit() );
+
+ } else if ( type == QLatin1String("domain") ) {
+ theDomains->append( list[1].lower().local8Bit() );
+ }
+ }
+ }
+
+ if (theNs->isEmpty()) {
+#if defined(Q_MODERN_RES_API)
+ struct __res_state res;
+ res_ninit( &res );
+ int i;
+ // find the name servers to use
+ for( i=0; i < MAXNS && i < res.nscount; i++ )
+ theNs->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) );
+# if defined(MAXDFLSRCH)
+ for( i=0; i < MAXDFLSRCH; i++ ) {
+ if ( res.dnsrch[i] && *(res.dnsrch[i]) )
+ theDomains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() );
+ else
+ break;
+ }
+# endif
+ if ( *res.defdname )
+ theDomains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() );
+#else
+ res_init();
+ int i;
+ // find the name servers to use
+ for( i=0; i < MAXNS && i < _res.nscount; i++ )
+ theNs->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) );
+# if defined(MAXDFLSRCH)
+ for( i=0; i < MAXDFLSRCH; i++ ) {
+ if ( _res.dnsrch[i] && *(_res.dnsrch[i]) )
+ theDomains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() );
+ else
+ break;
+ }
+# endif
+ if ( *_res.defdname )
+ theDomains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() );
+#endif
+
+ // the code above adds "0.0.0.0" as a name server at the slightest
+ // hint of trouble. so remove those again.
+ theNs->first();
+ while( theNs->current() ) {
+ if ( theNs->current()->isNull() )
+ delete theNs->take();
+ else
+ theNs->next();
+ }
+ }
+
+ QFile hosts( QString::fromLatin1( "/etc/hosts" ) );
+ if ( hosts.open( QIODevice::ReadOnly ) ) {
+ // read the /etc/hosts file, creating long-life A and PTR RRs
+ // for the things we find.
+ QTextStream i( &hosts );
+ QString line;
+ while( !i.atEnd() ) {
+ line = i.readLine().simplifyWhiteSpace().lower();
+ uint n = 0;
+ while( (int) n < line.length() && line[(int)n] != QLatin1Char('#') )
+ n++;
+ line.truncate( n );
+ n = 0;
+ while( (int) n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ QString ip = line.left( n );
+ QHostAddress a;
+ a.setAddress( ip );
+ if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) {
+ bool first = true;
+ line = line.mid( n+1 );
+ n = 0;
+ while( (int) n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ QString hostname = line.left( n );
+ // ### in case of bad syntax, hostname is invalid. do we care?
+ if ( n ) {
+ Q3DnsRR * rr = new Q3DnsRR( hostname );
+ if ( a.isIPv4Address() )
+ rr->t = Q3Dns::A;
+ else
+ rr->t = Q3Dns::Aaaa;
+ rr->address = a;
+ rr->deleteTime = UINT_MAX;
+ rr->expireTime = UINT_MAX;
+ rr->current = true;
+ if ( first ) {
+ first = false;
+ Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) );
+ ptr->t = Q3Dns::Ptr;
+ ptr->target = hostname;
+ ptr->deleteTime = UINT_MAX;
+ ptr->expireTime = UINT_MAX;
+ ptr->current = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DNS
diff --git a/src/qt3support/network/q3dns.h b/src/qt3support/network/q3dns.h
new file mode 100644
index 0000000..130f1f8
--- /dev/null
+++ b/src/qt3support/network/q3dns.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DNS_H
+#define Q3DNS_H
+
+#include <QtCore/qobject.h>
+#include <QtNetwork/qhostaddress.h>
+#include <QtCore/qsocketnotifier.h>
+#include <QtCore/qstringlist.h>
+#include <Qt3Support/q3valuelist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_DNS
+
+//#define Q_DNS_SYNCHRONOUS
+
+class Q3DnsPrivate;
+
+class Q_COMPAT_EXPORT Q3Dns: public QObject {
+ Q_OBJECT
+public:
+ enum RecordType {
+ None,
+ A, Aaaa,
+ Mx, Srv,
+ Cname,
+ Ptr,
+ Txt
+ };
+
+ Q3Dns();
+ Q3Dns( const QString & label, RecordType rr = A );
+ Q3Dns( const QHostAddress & address, RecordType rr = Ptr );
+ virtual ~Q3Dns();
+
+ // to set/change the query
+ virtual void setLabel( const QString & label );
+ virtual void setLabel( const QHostAddress & address );
+ QString label() const { return l; }
+
+ virtual void setRecordType( RecordType rr = A );
+ RecordType recordType() const { return t; }
+
+ // whether something is happening behind the scenes
+ bool isWorking() const;
+
+ // to query for replies
+ Q3ValueList<QHostAddress> addresses() const;
+
+ class Q_COMPAT_EXPORT MailServer {
+ public:
+ MailServer( const QString & n=QString(), Q_UINT16 p=0 )
+ :name(n), priority(p) {}
+ QString name;
+ Q_UINT16 priority;
+ Q_DUMMY_COMPARISON_OPERATOR(MailServer)
+ };
+ Q3ValueList<MailServer> mailServers() const;
+
+ class Q_COMPAT_EXPORT Server {
+ public:
+ Server(const QString & n=QString(), Q_UINT16 p=0, Q_UINT16 w=0, Q_UINT16 po=0 )
+ : name(n), priority(p), weight(w), port(po) {}
+ QString name;
+ Q_UINT16 priority;
+ Q_UINT16 weight;
+ Q_UINT16 port;
+ Q_DUMMY_COMPARISON_OPERATOR(Server)
+ };
+ Q3ValueList<Server> servers() const;
+
+ QStringList hostNames() const;
+
+ QStringList texts() const;
+
+ QString canonicalName() const; // ### real-world but uncommon: QStringList
+
+ QStringList qualifiedNames() const { return n; }
+
+#if defined(Q_DNS_SYNCHRONOUS)
+protected:
+ void connectNotify( const char *signal );
+#endif
+
+Q_SIGNALS:
+ void resultsReady();
+
+private Q_SLOTS:
+ void startQuery();
+
+private:
+ static void doResInit();
+ void setStartQueryTimer();
+ static QString toInAddrArpaDomain( const QHostAddress &address );
+#if defined(Q_DNS_SYNCHRONOUS)
+ void doSynchronousLookup();
+#endif
+
+ QString l;
+ QStringList n;
+ RecordType t;
+ Q3DnsPrivate * d;
+
+ friend class Q3DnsAnswer;
+ friend class Q3DnsManager;
+};
+
+
+// Q3DnsSocket are sockets that are used for DNS lookup
+
+class Q3DnsSocket: public QObject {
+ Q_OBJECT
+ // note: Private not public. This class contains NO public API.
+protected:
+ Q3DnsSocket( QObject *, const char * );
+ virtual ~Q3DnsSocket();
+
+private Q_SLOTS:
+ virtual void cleanCache();
+ virtual void retransmit();
+ virtual void answer();
+};
+
+#endif // QT_NO_DNS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DNS_H
diff --git a/src/qt3support/network/q3ftp.cpp b/src/qt3support/network/q3ftp.cpp
new file mode 100644
index 0000000..5a456e6
--- /dev/null
+++ b/src/qt3support/network/q3ftp.cpp
@@ -0,0 +1,2378 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "q3ftp.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+
+#include "q3socket.h"
+#include "q3socketdevice.h"
+#include "qurlinfo.h"
+#include "q3urloperator.h"
+#include "qstringlist.h"
+#include "qregexp.h"
+#include "qtimer.h"
+#include "qfileinfo.h"
+#include "q3ptrdict.h"
+#include "q3cstring.h"
+#include "qcoreapplication.h"
+#include "qftp.h"
+
+#ifndef QT_NO_TEXTCODEC
+#include "qtextcodec.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3FTPPI_DEBUG
+//#define Q3FTPDTP_DEBUG
+
+class Q3FtpPI;
+
+class Q3FtpDTP : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ConnectState {
+ CsHostFound,
+ CsConnected,
+ CsClosed,
+ CsHostNotFound,
+ CsConnectionRefused
+ };
+
+ Q3FtpDTP( Q3FtpPI *p, QObject *parent=0, const char *name=0 );
+
+ void setData( QByteArray * );
+ void setDevice( QIODevice * );
+ void writeData();
+
+ void setBytesTotal( int bytes )
+ {
+ bytesTotal = bytes;
+ bytesDone = 0;
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ }
+
+ bool hasError() const;
+ QString errorMessage() const;
+ void clearError();
+
+ void connectToHost( const QString & host, Q_UINT16 port )
+ { socket.connectToHost( host, port ); }
+
+ Q3Socket::State socketState() const
+ { return socket.state(); }
+
+ Q_ULONG bytesAvailable() const
+ { return socket.bytesAvailable(); }
+
+ Q_LONG readBlock( char *data, Q_ULONG maxlen )
+ {
+ Q_LONG read = socket.readBlock( data, maxlen );
+ bytesDone += read;
+ return read;
+ }
+
+ QByteArray readAll()
+ {
+ QByteArray tmp = socket.readAll();
+ bytesDone += tmp.size();
+ return tmp;
+ }
+
+ void abortConnection();
+
+ static bool parseDir( const QString &buffer, const QString &userName, QUrlInfo *info );
+
+signals:
+ void listInfo( const QUrlInfo& );
+ void readyRead();
+ void dataTransferProgress( int, int );
+
+ void connectState( int );
+
+private slots:
+ void socketConnected();
+ void socketReadyRead();
+ void socketError( int );
+ void socketConnectionClosed();
+ void socketBytesWritten( int );
+
+private:
+ void clearData()
+ {
+ is_ba = false;
+ data.dev = 0;
+ }
+
+ Q3Socket socket;
+ Q3FtpPI *pi;
+ QString err;
+ int bytesDone;
+ int bytesTotal;
+ bool callWriteData;
+
+ // If is_ba is true, ba is used; ba is never 0.
+ // Otherwise dev is used; dev can be 0 or not.
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+};
+
+class Q3FtpPI : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q3FtpPI( QObject *parent = 0 );
+
+ void connectToHost( const QString &host, Q_UINT16 port );
+
+ bool sendCommands( const QStringList &cmds );
+ bool sendCommand( const QString &cmd )
+ { return sendCommands( QStringList( cmd ) ); }
+
+ void clearPendingCommands();
+ void abort();
+
+ QString currentCommand() const
+ { return currentCmd; }
+
+ bool rawCommand;
+
+ Q3FtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it
+ // makes the design simpler this way
+signals:
+ void connectState( int );
+ void finished( const QString& );
+ void error( int, const QString& );
+ void rawFtpReply( int, const QString& );
+
+private slots:
+ void hostFound();
+ void connected();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void error( int );
+
+ void dtpConnectState( int );
+
+private:
+ // the states are modelled after the generalized state diagram of RFC 959,
+ // page 58
+ enum State {
+ Begin,
+ Idle,
+ Waiting,
+ Success,
+ Failure
+ };
+
+ enum AbortState {
+ None,
+ AbortStarted,
+ WaitForAbortToFinish
+ };
+
+ bool processReply();
+ bool startNextCmd();
+
+ Q3Socket commandSocket;
+ QString replyText;
+ signed char replyCode[3];
+ State state;
+ AbortState abortState;
+ QStringList pendingCommands;
+ QString currentCmd;
+
+ bool waitForDtpToConnect;
+ bool waitForDtpToClose;
+};
+
+/**********************************************************************
+ *
+ * Q3FtpCommand implemenatation
+ *
+ *********************************************************************/
+class Q3FtpCommand
+{
+public:
+ Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw );
+ Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, const QByteArray &ba );
+ Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, QIODevice *dev );
+ ~Q3FtpCommand();
+
+ int id;
+ Q3Ftp::Command command;
+ QStringList rawCmds;
+
+ // If is_ba is true, ba is used; ba is never 0.
+ // Otherwise dev is used; dev can be 0 or not.
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+
+ static int idCounter;
+};
+
+int Q3FtpCommand::idCounter = 0;
+
+Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw )
+ : command(cmd), rawCmds(raw), is_ba(false)
+{
+ id = ++idCounter;
+ data.dev = 0;
+}
+
+Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, const QByteArray &ba )
+ : command(cmd), rawCmds(raw), is_ba(true)
+{
+ id = ++idCounter;
+ data.ba = new QByteArray( ba );
+}
+
+Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, QIODevice *dev )
+ : command(cmd), rawCmds(raw), is_ba(false)
+{
+ id = ++idCounter;
+ data.dev = dev;
+}
+
+Q3FtpCommand::~Q3FtpCommand()
+{
+ if ( is_ba )
+ delete data.ba;
+}
+
+/**********************************************************************
+ *
+ * Q3FtpDTP implemenatation
+ *
+ *********************************************************************/
+Q3FtpDTP::Q3FtpDTP( Q3FtpPI *p, QObject *parent, const char *name ) :
+ QObject( parent, name ),
+ socket( 0, "Q3FtpDTP_socket" ),
+ pi( p ),
+ callWriteData( false )
+{
+ clearData();
+
+ connect( &socket, SIGNAL(connected()),
+ SLOT(socketConnected()) );
+ connect( &socket, SIGNAL(readyRead()),
+ SLOT(socketReadyRead()) );
+ connect( &socket, SIGNAL(error(int)),
+ SLOT(socketError(int)) );
+ connect( &socket, SIGNAL(connectionClosed()),
+ SLOT(socketConnectionClosed()) );
+ connect( &socket, SIGNAL(bytesWritten(int)),
+ SLOT(socketBytesWritten(int)) );
+}
+
+void Q3FtpDTP::setData( QByteArray *ba )
+{
+ is_ba = true;
+ data.ba = ba;
+}
+
+void Q3FtpDTP::setDevice( QIODevice *dev )
+{
+ is_ba = false;
+ data.dev = dev;
+}
+
+void Q3FtpDTP::writeData()
+{
+ if ( is_ba ) {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::writeData: write %d bytes", data.ba->size() );
+#endif
+ if ( data.ba->size() == 0 )
+ emit dataTransferProgress( 0, bytesTotal );
+ else
+ socket.writeBlock( data.ba->data(), data.ba->size() );
+ socket.close();
+ clearData();
+ } else if ( data.dev ) {
+ callWriteData = false;
+ const int blockSize = 16*1024;
+ char buf[blockSize];
+ while ( !data.dev->atEnd() && socket.bytesToWrite()==0 ) {
+ Q_LONG read = data.dev->readBlock( buf, blockSize );
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::writeData: writeBlock() of size %d bytes", (int)read );
+#endif
+ socket.writeBlock( buf, read );
+ if ( !data.dev )
+ return; // this can happen when a command is aborted
+ }
+ if ( data.dev->atEnd() ) {
+ if ( bytesDone==0 && socket.bytesToWrite()==0 )
+ emit dataTransferProgress( 0, bytesTotal );
+ socket.close();
+ clearData();
+ } else {
+ callWriteData = true;
+ }
+ }
+}
+
+inline bool Q3FtpDTP::hasError() const
+{
+ return !err.isNull();
+}
+
+inline QString Q3FtpDTP::errorMessage() const
+{
+ return err;
+}
+
+inline void Q3FtpDTP::clearError()
+{
+ err.clear();
+}
+
+void Q3FtpDTP::abortConnection()
+{
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::abortConnection" );
+#endif
+ callWriteData = false;
+ clearData();
+
+ socket.clearPendingData();
+ socket.close();
+}
+
+bool Q3FtpDTP::parseDir( const QString &buffer, const QString &userName, QUrlInfo *info )
+{
+ QStringList lst = QStringList::split( QLatin1String(" "), buffer );
+
+ if ( lst.count() < 9 )
+ return false;
+
+ QString tmp;
+
+ // permissions
+ tmp = lst[ 0 ];
+
+ if ( tmp[ 0 ] == QChar( QLatin1Char('d') ) ) {
+ info->setDir( true );
+ info->setFile( false );
+ info->setSymLink( false );
+ } else if ( tmp[ 0 ] == QChar( QLatin1Char('-') ) ) {
+ info->setDir( false );
+ info->setFile( true );
+ info->setSymLink( false );
+ } else if ( tmp[ 0 ] == QChar( QLatin1Char('l') ) ) {
+ info->setDir( true ); // #### todo
+ info->setFile( false );
+ info->setSymLink( true );
+ } else {
+ return false;
+ }
+
+ static int user = 0;
+ static int group = 1;
+ static int other = 2;
+ static int readable = 0;
+ static int writable = 1;
+ static int executable = 2;
+
+ bool perms[ 3 ][ 3 ];
+ perms[0][0] = (tmp[ 1 ] == QLatin1Char('r'));
+ perms[0][1] = (tmp[ 2 ] == QLatin1Char('w'));
+ perms[0][2] = (tmp[ 3 ] == QLatin1Char('x'));
+ perms[1][0] = (tmp[ 4 ] == QLatin1Char('r'));
+ perms[1][1] = (tmp[ 5 ] == QLatin1Char('w'));
+ perms[1][2] = (tmp[ 6 ] == QLatin1Char('x'));
+ perms[2][0] = (tmp[ 7 ] == QLatin1Char('r'));
+ perms[2][1] = (tmp[ 8 ] == QLatin1Char('w'));
+ perms[2][2] = (tmp[ 9 ] == QLatin1Char('x'));
+
+ // owner
+ tmp = lst[ 2 ];
+ info->setOwner( tmp );
+
+ // group
+ tmp = lst[ 3 ];
+ info->setGroup( tmp );
+
+ // ### not correct
+ info->setWritable( ( userName == info->owner() && perms[ user ][ writable ] ) ||
+ perms[ other ][ writable ] );
+ info->setReadable( ( userName == info->owner() && perms[ user ][ readable ] ) ||
+ perms[ other ][ readable ] );
+
+ int p = 0;
+ if ( perms[ user ][ readable ] )
+ p |= QUrlInfo::ReadOwner;
+ if ( perms[ user ][ writable ] )
+ p |= QUrlInfo::WriteOwner;
+ if ( perms[ user ][ executable ] )
+ p |= QUrlInfo::ExeOwner;
+ if ( perms[ group ][ readable ] )
+ p |= QUrlInfo::ReadGroup;
+ if ( perms[ group ][ writable ] )
+ p |= QUrlInfo::WriteGroup;
+ if ( perms[ group ][ executable ] )
+ p |= QUrlInfo::ExeGroup;
+ if ( perms[ other ][ readable ] )
+ p |= QUrlInfo::ReadOther;
+ if ( perms[ other ][ writable ] )
+ p |= QUrlInfo::WriteOther;
+ if ( perms[ other ][ executable ] )
+ p |= QUrlInfo::ExeOther;
+ info->setPermissions( p );
+
+ // size
+ tmp = lst[ 4 ];
+ info->setSize( tmp.toInt() );
+
+ // date and time
+ QTime time;
+ QString dateStr;
+ dateStr += QLatin1String("Sun ");
+ lst[ 5 ][ 0 ] = lst[ 5 ][ 0 ].upper();
+ dateStr += lst[ 5 ];
+ dateStr += QLatin1Char(' ');
+ dateStr += lst[ 6 ];
+ dateStr += QLatin1Char(' ');
+
+ if ( lst[ 7 ].contains( QLatin1Char(':') ) ) {
+ time = QTime( lst[ 7 ].left( 2 ).toInt(), lst[ 7 ].right( 2 ).toInt() );
+ dateStr += QString::number( QDate::currentDate().year() );
+ } else {
+ dateStr += lst[ 7 ];
+ }
+
+ QDate date = QDate::fromString( dateStr );
+ info->setLastModified( QDateTime( date, time ) );
+
+ if ( lst[ 7 ].contains( QLatin1Char(':') ) ) {
+ const int futureTolerance = 600;
+ if( info->lastModified().secsTo( QDateTime::currentDateTime() ) < -futureTolerance ) {
+ QDateTime dt = info->lastModified();
+ QDate d = dt.date();
+ d.setYMD(d.year()-1, d.month(), d.day());
+ dt.setDate(d);
+ info->setLastModified(dt);
+ }
+ }
+
+ // name
+ if ( info->isSymLink() )
+ info->setName( lst[ 8 ].stripWhiteSpace() );
+ else {
+ QString n;
+ for ( uint i = 8; i < (uint) lst.count(); ++i )
+ n += lst[ i ] + QLatin1Char(' ');
+ n = n.stripWhiteSpace();
+ info->setName( n );
+ }
+ return true;
+}
+
+void Q3FtpDTP::socketConnected()
+{
+#if !defined (Q_WS_QWS)
+ // Use a large send buffer to reduce the number
+ // of writeBlocks when download and uploading files.
+ // The actual size used here (128k) is default on most
+ // Unixes.
+ socket.socketDevice()->setSendBufferSize(128 * 1024);
+ socket.socketDevice()->setReceiveBufferSize(128 * 1024);
+#endif
+
+ bytesDone = 0;
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsConnected )" );
+#endif
+ emit connectState( Q3FtpDTP::CsConnected );
+}
+
+void Q3FtpDTP::socketReadyRead()
+{
+ if ( pi->currentCommand().isEmpty() ) {
+ socket.close();
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsClosed )" );
+#endif
+ emit connectState( Q3FtpDTP::CsClosed );
+ return;
+ }
+
+ if ( pi->currentCommand().startsWith(QLatin1String("LIST")) ) {
+ while ( socket.canReadLine() ) {
+ QUrlInfo i;
+ QString line = QLatin1String(socket.readLine());
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP read (list): '%s'", line.latin1() );
+#endif
+ if ( parseDir( line, QLatin1String(""), &i ) ) {
+ emit listInfo( i );
+ } else {
+ // some FTP servers don't return a 550 if the file or directory
+ // does not exist, but rather write a text to the data socket
+ // -- try to catch these cases
+ if ( line.endsWith( QLatin1String("No such file or directory\r\n") ) )
+ err = line;
+ }
+ }
+ } else {
+ if ( !is_ba && data.dev ) {
+ QByteArray ba( socket.bytesAvailable() );
+ Q_LONG bytesRead = socket.readBlock( ba.data(), ba.size() );
+ if ( bytesRead < 0 ) {
+ // ### error handling
+ return;
+ }
+ ba.resize( bytesRead );
+ bytesDone += bytesRead;
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP read: %d bytes (total %d bytes)", (int)bytesRead, bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ if (data.dev) // make sure it wasn't deleted in the slot
+ data.dev->writeBlock( ba );
+ } else {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP readyRead: %d bytes available (total %d bytes read)", (int)bytesAvailable(), bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone+socket.bytesAvailable(), bytesTotal );
+ emit readyRead();
+ }
+ }
+}
+
+void Q3FtpDTP::socketError( int e )
+{
+ if ( e == Q3Socket::ErrHostNotFound ) {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsHostNotFound )" );
+#endif
+ emit connectState( Q3FtpDTP::CsHostNotFound );
+ } else if ( e == Q3Socket::ErrConnectionRefused ) {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsConnectionRefused )" );
+#endif
+ emit connectState( Q3FtpDTP::CsConnectionRefused );
+ }
+}
+
+void Q3FtpDTP::socketConnectionClosed()
+{
+ if ( !is_ba && data.dev ) {
+ clearData();
+ }
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsClosed )" );
+#endif
+ emit connectState( Q3FtpDTP::CsClosed );
+}
+
+void Q3FtpDTP::socketBytesWritten( int bytes )
+{
+ bytesDone += bytes;
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::bytesWritten( %d )", bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ if ( callWriteData )
+ writeData();
+}
+
+/**********************************************************************
+ *
+ * Q3FtpPI implemenatation
+ *
+ *********************************************************************/
+Q3FtpPI::Q3FtpPI( QObject *parent ) :
+ QObject( parent ),
+ rawCommand(false),
+ dtp( this ),
+ commandSocket( 0, "Q3FtpPI_socket" ),
+ state( Begin ), abortState( None ),
+ currentCmd( QString() ),
+ waitForDtpToConnect( false ),
+ waitForDtpToClose( false )
+{
+ connect( &commandSocket, SIGNAL(hostFound()),
+ SLOT(hostFound()) );
+ connect( &commandSocket, SIGNAL(connected()),
+ SLOT(connected()) );
+ connect( &commandSocket, SIGNAL(connectionClosed()),
+ SLOT(connectionClosed()) );
+ connect( &commandSocket, SIGNAL(delayedCloseFinished()),
+ SLOT(delayedCloseFinished()) );
+ connect( &commandSocket, SIGNAL(readyRead()),
+ SLOT(readyRead()) );
+ connect( &commandSocket, SIGNAL(error(int)),
+ SLOT(error(int)) );
+
+ connect( &dtp, SIGNAL(connectState(int)),
+ SLOT(dtpConnectState(int)) );
+}
+
+void Q3FtpPI::connectToHost( const QString &host, Q_UINT16 port )
+{
+ emit connectState( Q3Ftp::HostLookup );
+ commandSocket.connectToHost( host, port );
+}
+
+/*
+ Sends the sequence of commands \a cmds to the FTP server. When the commands
+ are all done the finished() signal is emitted. When an error occurs, the
+ error() signal is emitted.
+
+ If there are pending commands in the queue this functions returns false and
+ the \a cmds are not added to the queue; otherwise it returns true.
+*/
+bool Q3FtpPI::sendCommands( const QStringList &cmds )
+{
+ if ( !pendingCommands.isEmpty() )
+ return false;
+
+ if ( commandSocket.state()!=Q3Socket::Connected || state!=Idle ) {
+ emit error( Q3Ftp::NotConnected, QFtp::tr( "Not connected" ) );
+ return true; // there are no pending commands
+ }
+
+ pendingCommands = cmds;
+ startNextCmd();
+ return true;
+}
+
+void Q3FtpPI::clearPendingCommands()
+{
+ pendingCommands.clear();
+ dtp.abortConnection();
+ currentCmd.clear();
+ state = Idle;
+}
+
+void Q3FtpPI::abort()
+{
+ pendingCommands.clear();
+
+ if ( abortState != None )
+ // ABOR already sent
+ return;
+
+ abortState = AbortStarted;
+#if defined(Q3FTPPI_DEBUG)
+ qDebug( "Q3FtpPI send: ABOR" );
+#endif
+ commandSocket.writeBlock( "ABOR\r\n", 6 );
+
+ if ( currentCmd.startsWith(QLatin1String("STOR ")) )
+ dtp.abortConnection();
+}
+
+void Q3FtpPI::hostFound()
+{
+ emit connectState( Q3Ftp::Connecting );
+}
+
+void Q3FtpPI::connected()
+{
+ state = Begin;
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [connected()]", state );
+#endif
+ emit connectState( Q3Ftp::Connected );
+}
+
+void Q3FtpPI::connectionClosed()
+{
+ commandSocket.close();
+ emit connectState( Q3Ftp::Unconnected );
+}
+
+void Q3FtpPI::delayedCloseFinished()
+{
+ emit connectState( Q3Ftp::Unconnected );
+}
+
+void Q3FtpPI::error( int e )
+{
+ if ( e == Q3Socket::ErrHostNotFound ) {
+ emit connectState( Q3Ftp::Unconnected );
+ emit error( Q3Ftp::HostNotFound,
+ QFtp::tr( "Host %1 not found" ).arg( commandSocket.peerName() ) );
+ } else if ( e == Q3Socket::ErrConnectionRefused ) {
+ emit connectState( Q3Ftp::Unconnected );
+ emit error( Q3Ftp::ConnectionRefused,
+ QFtp::tr( "Connection refused to host %1" ).arg( commandSocket.peerName() ) );
+ }
+}
+
+void Q3FtpPI::readyRead()
+{
+ if ( waitForDtpToClose )
+ return;
+
+ while ( commandSocket.canReadLine() ) {
+ // read line with respect to line continuation
+ QString line = QLatin1String(commandSocket.readLine());
+ if ( replyText.isEmpty() ) {
+ if ( line.length() < 3 ) {
+ // ### protocol error
+ return;
+ }
+ const int lowerLimit[3] = {1,0,0};
+ const int upperLimit[3] = {5,5,9};
+ for ( int i=0; i<3; i++ ) {
+ replyCode[i] = line[i].digitValue();
+ if ( replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i] ) {
+ // ### protocol error
+ return;
+ }
+ }
+ }
+ QString endOfMultiLine;
+ endOfMultiLine[0] = '0' + replyCode[0];
+ endOfMultiLine[1] = '0' + replyCode[1];
+ endOfMultiLine[2] = '0' + replyCode[2];
+ endOfMultiLine[3] = ' ';
+ QString lineCont( endOfMultiLine );
+ lineCont[3] = '-';
+ QString lineLeft4 = line.left(4);
+
+ while ( lineLeft4 != endOfMultiLine ) {
+ if ( lineLeft4 == lineCont )
+ replyText += line.mid( 4 ); // strip 'xyz-'
+ else
+ replyText += line;
+ if ( !commandSocket.canReadLine() )
+ return;
+ line = QLatin1String(commandSocket.readLine());
+ lineLeft4 = line.left(4);
+ }
+ replyText += line.mid( 4 ); // strip reply code 'xyz '
+ if ( replyText.endsWith(QLatin1String("\r\n")) )
+ replyText.truncate( replyText.length()-2 );
+
+ if ( processReply() )
+ replyText = QLatin1String("");
+ }
+}
+
+/*
+ Process a reply from the FTP server.
+
+ Returns true if the reply was processed or false if the reply has to be
+ processed at a later point.
+*/
+bool Q3FtpPI::processReply()
+{
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [processReply() begin]", state );
+ if ( replyText.length() < 400 )
+ qDebug( "Q3FtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.latin1() );
+ else
+ qDebug( "Q3FtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2] );
+#endif
+
+ // process 226 replies ("Closing Data Connection") only when the data
+ // connection is really closed to avoid short reads of the DTP
+ if ( 100*replyCode[0]+10*replyCode[1]+replyCode[2] == 226 ) {
+ if ( dtp.socketState() != Q3Socket::Idle ) {
+ waitForDtpToClose = true;
+ return false;
+ }
+ }
+
+ switch ( abortState ) {
+ case AbortStarted:
+ abortState = WaitForAbortToFinish;
+ break;
+ case WaitForAbortToFinish:
+ abortState = None;
+ return true;
+ default:
+ break;
+ }
+
+ // get new state
+ static const State table[5] = {
+ /* 1yz 2yz 3yz 4yz 5yz */
+ Waiting, Success, Idle, Failure, Failure
+ };
+ switch ( state ) {
+ case Begin:
+ if ( replyCode[0] == 1 ) {
+ return true;
+ } else if ( replyCode[0] == 2 ) {
+ state = Idle;
+ emit finished( QFtp::tr( "Connected to host %1" ).arg( commandSocket.peerName() ) );
+ break;
+ }
+ // ### error handling
+ return true;
+ case Waiting:
+ if ( replyCode[0]<0 || replyCode[0]>5 )
+ state = Failure;
+ else
+#if defined(Q_OS_IRIX) && defined(Q_CC_GNU)
+ {
+ // work around a crash on 64 bit gcc IRIX
+ State *t = (State *) table;
+ state = t[replyCode[0] - 1];
+ }
+#else
+ state = table[replyCode[0] - 1];
+#endif
+ break;
+ default:
+ // ### spontaneous message
+ return true;
+ }
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [processReply() intermediate]", state );
+#endif
+
+ // special actions on certain replies
+ int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
+ emit rawFtpReply( replyCodeInt, replyText );
+ if ( rawCommand ) {
+ rawCommand = false;
+ } else if ( replyCodeInt == 227 ) {
+ // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
+ // rfc959 does not define this response precisely, and gives
+ // both examples where the parenthesis are used, and where
+ // they are missing. We need to scan for the address and host
+ // info.
+ QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));
+ if (addrPortPattern.search(replyText) == -1) {
+#if defined(Q3FTPPI_DEBUG)
+ qDebug( "Q3Ftp: bad 227 response -- address and port information missing" );
+#endif
+ // ### error handling
+ } else {
+ QStringList lst = addrPortPattern.capturedTexts();
+ QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];
+ Q_UINT16 port = ( lst[5].toUInt() << 8 ) + lst[6].toUInt();
+ waitForDtpToConnect = true;
+ dtp.connectToHost( host, port );
+ }
+ } else if ( replyCodeInt == 230 ) {
+ if ( currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&
+ pendingCommands.first().startsWith(QLatin1String("PASS ")) ) {
+ // no need to send the PASS -- we are already logged in
+ pendingCommands.pop_front();
+ }
+ // 230 User logged in, proceed.
+ emit connectState( Q3Ftp::LoggedIn );
+ } else if ( replyCodeInt == 213 ) {
+ // 213 File status.
+ if ( currentCmd.startsWith(QLatin1String("SIZE ")) )
+ dtp.setBytesTotal( replyText.simplifyWhiteSpace().toInt() );
+ } else if ( replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR ")) ) {
+ dtp.writeData();
+ }
+
+ // react on new state
+ switch ( state ) {
+ case Begin:
+ // ### should never happen
+ break;
+ case Success:
+ // ### success handling
+ state = Idle;
+ // no break!
+ case Idle:
+ if ( dtp.hasError() ) {
+ emit error( Q3Ftp::UnknownError, dtp.errorMessage() );
+ dtp.clearError();
+ }
+ startNextCmd();
+ break;
+ case Waiting:
+ // ### do nothing
+ break;
+ case Failure:
+ emit error( Q3Ftp::UnknownError, replyText );
+ state = Idle;
+ startNextCmd();
+ break;
+ }
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [processReply() end]", state );
+#endif
+ return true;
+}
+
+#ifndef QT_NO_TEXTCODEC
+Q_COMPAT_EXPORT QTextCodec *qt_ftp_filename_codec = 0;
+#endif
+
+/*
+ Starts next pending command. Returns false if there are no pending commands,
+ otherwise it returns true.
+*/
+bool Q3FtpPI::startNextCmd()
+{
+ if ( waitForDtpToConnect )
+ // don't process any new commands until we are connected
+ return true;
+
+#if defined(Q3FTPPI_DEBUG)
+ if ( state != Idle )
+ qDebug( "Q3FtpPI startNextCmd: Internal error! Q3FtpPI called in non-Idle state %d", state );
+#endif
+ if ( pendingCommands.isEmpty() ) {
+ currentCmd.clear();
+ emit finished( replyText );
+ return false;
+ }
+ currentCmd = pendingCommands.first();
+ pendingCommands.pop_front();
+#if defined(Q3FTPPI_DEBUG)
+ qDebug( "Q3FtpPI send: %s", currentCmd.left( currentCmd.length()-2 ).latin1() );
+#endif
+ state = Waiting;
+#ifndef QT_NO_TEXTCODEC
+ if ( qt_ftp_filename_codec ) {
+ int len;
+ Q3CString enc = qt_ftp_filename_codec->fromUnicode(currentCmd,len);
+ commandSocket.writeBlock( enc.data(), len );
+ } else
+#endif
+ {
+ commandSocket.writeBlock( currentCmd.latin1(), currentCmd.length() );
+ }
+ return true;
+}
+
+void Q3FtpPI::dtpConnectState( int s )
+{
+ switch ( s ) {
+ case Q3FtpDTP::CsClosed:
+ if ( waitForDtpToClose ) {
+ // there is an unprocessed reply
+ if ( processReply() )
+ replyText = QLatin1String("");
+ else
+ return;
+ }
+ waitForDtpToClose = false;
+ readyRead();
+ return;
+ case Q3FtpDTP::CsConnected:
+ waitForDtpToConnect = false;
+ startNextCmd();
+ return;
+ case Q3FtpDTP::CsHostNotFound:
+ case Q3FtpDTP::CsConnectionRefused:
+ emit error( Q3Ftp::ConnectionRefused,
+ QFtp::tr( "Connection refused for data connection" ) );
+ startNextCmd();
+ return;
+ default:
+ return;
+ }
+}
+
+/**********************************************************************
+ *
+ * Q3FtpPrivate
+ *
+ *********************************************************************/
+class Q3FtpPrivate
+{
+public:
+ Q3FtpPrivate() :
+ close_waitForStateChange(false),
+ state( Q3Ftp::Unconnected ),
+ error( Q3Ftp::NoError ),
+ npWaitForLoginDone( false )
+ { pending.setAutoDelete( true ); }
+
+ Q3FtpPI pi;
+ Q3PtrList<Q3FtpCommand> pending;
+ bool close_waitForStateChange;
+ Q3Ftp::State state;
+ Q3Ftp::Error error;
+ QString errorString;
+
+ bool npWaitForLoginDone;
+};
+
+static Q3PtrDict<Q3FtpPrivate> *d_ptr = 0;
+static void cleanup_d_ptr()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+static Q3FtpPrivate* dHelper( const Q3Ftp* foo )
+{
+ if ( !d_ptr ) {
+ d_ptr = new Q3PtrDict<Q3FtpPrivate>;
+ d_ptr->setAutoDelete( true );
+ qAddPostRoutine( cleanup_d_ptr );
+ }
+ Q3FtpPrivate* ret = d_ptr->find( (void*)foo );
+ if ( ! ret ) {
+ ret = new Q3FtpPrivate;
+ d_ptr->replace( (void*) foo, ret );
+ }
+ return ret;
+}
+
+static void delete_d( const Q3Ftp* foo )
+{
+ if ( d_ptr )
+ d_ptr->remove( (void*) foo );
+}
+
+/**********************************************************************
+ *
+ * Q3Ftp implementation
+ *
+ *********************************************************************/
+/*!
+ \class Q3Ftp
+ \brief The Q3Ftp class provides an implementation of the FTP protocol.
+
+ \compat
+
+ This class provides two different interfaces: one is the
+ QNetworkProtocol interface that allows you to use FTP through the
+ QUrlOperator abstraction. The other is a direct interface to FTP
+ that gives you lower-level access to the FTP protocol for finer
+ control. Using the direct interface you can also execute arbitrary
+ FTP commands.
+
+ Don't mix the two interfaces, since the behavior is not
+ well-defined.
+
+ If you want to use Q3Ftp with the QNetworkProtocol interface, you
+ do not use it directly, but rather through a QUrlOperator, for
+ example:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 0
+
+ This code will only work if the Q3Ftp class is registered; to
+ register the class, you must call q3InitNetworkProtocols() before
+ using a QUrlOperator with Q3Ftp.
+
+ The rest of this descrption describes the direct interface to FTP.
+
+ The class works asynchronously, so there are no blocking
+ functions. If an operation cannot be executed immediately, the
+ function will still return straight away and the operation will be
+ scheduled for later execution. The results of scheduled operations
+ are reported via signals. This approach depends on the event loop
+ being in operation.
+
+ The operations that can be scheduled (they are called "commands"
+ in the rest of the documentation) are the following:
+ connectToHost(), login(), close(), list(), cd(), get(), put(),
+ remove(), mkdir(), rmdir(), rename() and rawCommand().
+
+ All of these commands return a unique identifier that allows you
+ to keep track of the command that is currently being executed.
+ When the execution of a command starts, the commandStarted()
+ signal with the command's identifier is emitted. When the command
+ is finished, the commandFinished() signal is emitted with the
+ command's identifier and a bool that indicates whether the command
+ finished with an error.
+
+ In some cases, you might want to execute a sequence of commands,
+ e.g. if you want to connect and login to a FTP server. This is
+ simply achieved:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 1
+
+ In this case two FTP commands have been scheduled. When the last
+ scheduled command has finished, a done() signal is emitted with
+ a bool argument that tells you whether the sequence finished with
+ an error.
+
+ If an error occurs during the execution of one of the commands in
+ a sequence of commands, all the pending commands (i.e. scheduled,
+ but not yet executed commands) are cleared and no signals are
+ emitted for them.
+
+ Some commands, e.g. list(), emit additional signals to report
+ their results.
+
+ Example: If you want to download the INSTALL file from the Qt
+ FTP server, you would write this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 2
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 3
+
+ The dataTransferProgress() signal in the above example is useful
+ if you want to show a \link QProgressBar progress bar \endlink to
+ inform the user about the progress of the download. The
+ readyRead() signal tells you that there is data ready to be read.
+ The amount of data can be queried then with the bytesAvailable()
+ function and it can be read with the readBlock() or readAll()
+ function.
+
+ If the login fails for the above example, the signals would look
+ like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 4
+
+ You can then get details about the error with the error() and
+ errorString() functions.
+
+ The functions currentId() and currentCommand() provide more
+ information about the currently executing command.
+
+ The functions hasPendingCommands() and clearPendingCommands()
+ allow you to query and clear the list of pending commands.
+
+ The safest and easiest way to use the FTP protocol is to use
+ QUrlOperator() or the FTP commands described above. If you are an
+ experienced network programmer and want to have complete control
+ you can use rawCommand() to execute arbitrary FTP commands.
+
+ \sa Q3NetworkProtocol, Q3UrlOperator Q3Http
+*/
+
+/*!
+ Constructs a Q3Ftp object.
+*/
+Q3Ftp::Q3Ftp() : Q3NetworkProtocol()
+{
+ init();
+}
+
+/*!
+ Constructs a Q3Ftp object. The \a parent and \a name parameters
+ are passed to the QObject constructor.
+*/
+Q3Ftp::Q3Ftp( QObject *parent, const char *name ) : Q3NetworkProtocol()
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+}
+
+void Q3Ftp::init()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ d->errorString = QFtp::tr( "Unknown error" );
+
+ connect( &d->pi, SIGNAL(connectState(int)),
+ SLOT(piConnectState(int)) );
+ connect( &d->pi, SIGNAL(finished(QString)),
+ SLOT(piFinished(QString)) );
+ connect( &d->pi, SIGNAL(error(int,QString)),
+ SLOT(piError(int,QString)) );
+ connect( &d->pi, SIGNAL(rawFtpReply(int,QString)),
+ SLOT(piFtpReply(int,QString)) );
+
+ connect( &d->pi.dtp, SIGNAL(readyRead()),
+ SIGNAL(readyRead()) );
+ connect( &d->pi.dtp, SIGNAL(dataTransferProgress(int,int)),
+ SIGNAL(dataTransferProgress(int,int)) );
+ connect( &d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
+ SIGNAL(listInfo(QUrlInfo)) );
+}
+
+/*!
+ \enum Q3Ftp::State
+
+ This enum defines the connection state:
+
+ \value Unconnected There is no connection to the host.
+ \value HostLookup A host name lookup is in progress.
+ \value Connecting An attempt to connect to the host is in progress.
+ \value Connected Connection to the host has been achieved.
+ \value LoggedIn Connection and user login have been achieved.
+ \value Closing The connection is closing down, but it is not yet
+ closed. (The state will be \c Unconnected when the connection is
+ closed.)
+
+ \sa stateChanged() state()
+*/
+/*!
+ \enum Q3Ftp::Error
+
+ This enum identifies the error that occurred.
+
+ \value NoError No error occurred.
+ \value HostNotFound The host name lookup failed.
+ \value ConnectionRefused The server refused the connection.
+ \value NotConnected Tried to send a command, but there is no connection to
+ a server.
+ \value UnknownError An error other than those specified above
+ occurred.
+
+ \sa error()
+*/
+
+/*!
+ \enum Q3Ftp::Command
+
+ This enum is used as the return value for the currentCommand() function.
+ This allows you to perform specific actions for particular
+ commands, e.g. in a FTP client, you might want to clear the
+ directory view when a list() command is started; in this case you
+ can simply check in the slot connected to the start() signal if
+ the currentCommand() is \c List.
+
+ \value None No command is being executed.
+ \value ConnectToHost connectToHost() is being executed.
+ \value Login login() is being executed.
+ \value Close close() is being executed.
+ \value List list() is being executed.
+ \value Cd cd() is being executed.
+ \value Get get() is being executed.
+ \value Put put() is being executed.
+ \value Remove remove() is being executed.
+ \value Mkdir mkdir() is being executed.
+ \value Rmdir rmdir() is being executed.
+ \value Rename rename() is being executed.
+ \value RawCommand rawCommand() is being executed.
+
+ \sa currentCommand()
+*/
+
+/*!
+ \fn void Q3Ftp::stateChanged( int state )
+
+ This signal is emitted when the state of the connection changes.
+ The argument \a state is the new state of the connection; it is
+ one of the \l State values.
+
+ It is usually emitted in response to a connectToHost() or close()
+ command, but it can also be emitted "spontaneously", e.g. when the
+ server closes the connection unexpectedly.
+
+ \sa connectToHost() close() state() State
+*/
+
+/*!
+ \fn void Q3Ftp::listInfo( const QUrlInfo &i );
+
+ This signal is emitted for each directory entry the list() command
+ finds. The details of the entry are stored in \a i.
+
+ \sa list()
+*/
+
+/*!
+ \fn void Q3Ftp::commandStarted( int id )
+
+ This signal is emitted when processing the command identified by
+ \a id starts.
+
+ \sa commandFinished() done()
+*/
+
+/*!
+ \fn void Q3Ftp::commandFinished( int id, bool error )
+
+ This signal is emitted when processing the command identified by
+ \a id has finished. \a error is true if an error occurred during
+ the processing; otherwise \a error is false.
+
+ \sa commandStarted() done() error() errorString()
+*/
+
+/*!
+ \fn void Q3Ftp::done( bool error )
+
+ This signal is emitted when the last pending command has finished;
+ (it is emitted after the last command's commandFinished() signal).
+ \a error is true if an error occurred during the processing;
+ otherwise \a error is false.
+
+ \sa commandFinished() error() errorString()
+*/
+
+/*!
+ \fn void Q3Ftp::readyRead()
+
+ This signal is emitted in response to a get() command when there
+ is new data to read.
+
+ If you specify a device as the second argument in the get()
+ command, this signal is \e not emitted; instead the data is
+ written directly to the device.
+
+ You can read the data with the readAll() or readBlock() functions.
+
+ This signal is useful if you want to process the data in chunks as
+ soon as it becomes available. If you are only interested in the
+ complete data, just connect to the commandFinished() signal and
+ read the data then instead.
+
+ \sa get() readBlock() readAll() bytesAvailable()
+*/
+
+/*!
+ \fn void Q3Ftp::dataTransferProgress( int done, int total )
+
+ This signal is emitted in response to a get() or put() request to
+ indicate the current progress of the download or upload.
+
+ \a done is the amount of data that has already been transferred
+ and \a total is the total amount of data to be read or written. It
+ is possible that the Q3Ftp class is not able to determine the total
+ amount of data that should be transferred, in which case \a total
+ is 0. (If you connect this signal to a QProgressBar, the progress
+ bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa get() put()
+*/
+
+/*!
+ \fn void Q3Ftp::rawCommandReply( int replyCode, const QString &detail );
+
+ This signal is emitted in response to the rawCommand() function.
+ \a replyCode is the 3 digit reply code and \a detail is the text
+ that follows the reply code.
+
+ \sa rawCommand()
+*/
+
+/*!
+ Connects to the FTP server \a host using port \a port.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c HostLookup, then \c
+ Connecting, then \c Connected.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa stateChanged() commandStarted() commandFinished()
+*/
+int Q3Ftp::connectToHost( const QString &host, Q_UINT16 port )
+{
+ QStringList cmds;
+ cmds << host;
+ cmds << QString::number( (uint)port );
+ return addCommand( new Q3FtpCommand( ConnectToHost, cmds ) );
+}
+
+/*!
+ Logs in to the FTP server with the username \a user and the
+ password \a password.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c LoggedIn.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::login( const QString &user, const QString &password )
+{
+ QStringList cmds;
+ cmds << ( QString::fromLatin1("USER ") + ( user.isNull() ? QString::fromLatin1("anonymous") : user ) + QLatin1String("\r\n") );
+ cmds << ( QString::fromLatin1("PASS ") + ( password.isNull() ? QString::fromLatin1("anonymous@") : password ) + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Login, cmds ) );
+}
+
+/*!
+ Closes the connection to the FTP server.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c Closing, then \c
+ Unconnected.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa stateChanged() commandStarted() commandFinished()
+*/
+int Q3Ftp::close()
+{
+ return addCommand( new Q3FtpCommand( Close, QStringList(QLatin1String("QUIT\r\n")) ) );
+}
+
+/*!
+ Lists the contents of directory \a dir on the FTP server. If \a
+ dir is empty, it lists the contents of the current directory.
+
+ The listInfo() signal is emitted for each directory entry found.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa listInfo() commandStarted() commandFinished()
+*/
+int Q3Ftp::list( const QString &dir )
+{
+ QStringList cmds;
+ cmds << QLatin1String("TYPE A\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ if ( dir.isEmpty() )
+ cmds << QLatin1String("LIST\r\n");
+ else
+ cmds << ( QLatin1String("LIST ") + dir + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( List, cmds ) );
+}
+
+/*!
+ Changes the working directory of the server to \a dir.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::cd( const QString &dir )
+{
+ return addCommand( new Q3FtpCommand( Cd, QStringList(QLatin1String("CWD ")+dir+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Downloads the file \a file from the server.
+
+ If \a dev is 0, then the readyRead() signal is emitted when there
+ is data available to read. You can then read the data with the
+ readBlock() or readAll() functions.
+
+ If \a dev is not 0, the data is written directly to the device \a
+ dev. Make sure that the \a dev pointer is valid for the duration
+ of the operation (it is safe to delete it when the
+ commandFinished() signal is emitted). In this case the readyRead()
+ signal is \e not emitted and you cannot read data with the
+ readBlock() or readAll() functions.
+
+ If you don't read the data immediately it becomes available, i.e.
+ when the readyRead() signal is emitted, it is still available
+ until the next command is started.
+
+ For example, if you want to present the data to the user as soon
+ as there is something available, connect to the readyRead() signal
+ and read the data immediately. On the other hand, if you only want
+ to work with the complete data, you can connect to the
+ commandFinished() signal and read the data when the get() command
+ is finished.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa readyRead() dataTransferProgress() commandStarted()
+ commandFinished()
+*/
+int Q3Ftp::get( const QString &file, QIODevice *dev )
+{
+ QStringList cmds;
+ cmds << ( QLatin1String("SIZE ") + file + QLatin1String("\r\n") );
+ cmds << QLatin1String("TYPE I\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ cmds << ( QLatin1String("RETR ") + file + QLatin1String("\r\n") );
+ if ( dev )
+ return addCommand( new Q3FtpCommand( Get, cmds, dev ) );
+ return addCommand( new Q3FtpCommand( Get, cmds ) );
+}
+
+/*!
+ \overload
+
+ Writes the data \a data to the file called \a file on the server.
+ The progress of the upload is reported by the
+ dataTransferProgress() signal.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa dataTransferProgress() commandStarted() commandFinished()
+*/
+int Q3Ftp::put( const QByteArray &data, const QString &file )
+{
+ QStringList cmds;
+ cmds << QLatin1String("TYPE I\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ cmds << ( QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n") );
+ cmds << ( QLatin1String("STOR ") + file + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Put, cmds, data ) );
+}
+
+/*!
+ Reads the data from the IO device \a dev, and writes it to the
+ file called \a file on the server. The data is read in chunks from
+ the IO device, so this overload allows you to transmit large
+ amounts of data without the need to read all the data into memory
+ at once.
+
+ Make sure that the \a dev pointer is valid for the duration of the
+ operation (it is safe to delete it when the commandFinished() is
+ emitted).
+*/
+int Q3Ftp::put( QIODevice *dev, const QString &file )
+{
+ QStringList cmds;
+ cmds << QLatin1String("TYPE I\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ if ( !dev->isSequentialAccess() )
+ cmds << ( QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n") );
+ cmds << ( QLatin1String("STOR ") + file + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Put, cmds, dev ) );
+}
+
+/*!
+ Deletes the file called \a file from the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::remove( const QString &file )
+{
+ return addCommand( new Q3FtpCommand( Remove, QStringList(QLatin1String("DELE ")+file+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Creates a directory called \a dir on the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::mkdir( const QString &dir )
+{
+ return addCommand( new Q3FtpCommand( Mkdir, QStringList(QLatin1String("MKD ")+dir+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Removes the directory called \a dir from the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::rmdir( const QString &dir )
+{
+ return addCommand( new Q3FtpCommand( Rmdir, QStringList(QLatin1String("RMD ")+dir+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Renames the file called \a oldname to \a newname on the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::rename( const QString &oldname, const QString &newname )
+{
+ QStringList cmds;
+ cmds << ( QLatin1String("RNFR ") + oldname + QLatin1String("\r\n") );
+ cmds << ( QLatin1String("RNTO ") + newname + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Rename, cmds ) );
+}
+
+/*!
+ Sends the raw FTP command \a command to the FTP server. This is
+ useful for low-level FTP access. If the operation you wish to
+ perform has an equivalent Q3Ftp function, we recommend using the
+ function instead of raw FTP commands since the functions are
+ easier and safer.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa rawCommandReply() commandStarted() commandFinished()
+*/
+int Q3Ftp::rawCommand( const QString &command )
+{
+ QString cmd = command.stripWhiteSpace() + QLatin1String("\r\n");
+ return addCommand( new Q3FtpCommand( RawCommand, QStringList(cmd) ) );
+}
+
+/*!
+ Returns the number of bytes that can be read from the data socket
+ at the moment.
+
+ \sa get() readyRead() readBlock() readAll()
+*/
+Q_ULONG Q3Ftp::bytesAvailable() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pi.dtp.bytesAvailable();
+}
+
+/*!
+ Reads \a maxlen bytes from the data socket into \a data and
+ returns the number of bytes read. Returns -1 if an error occurred.
+
+ \sa get() readyRead() bytesAvailable() readAll()
+*/
+Q_LONG Q3Ftp::readBlock( char *data, Q_ULONG maxlen )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pi.dtp.readBlock( data, maxlen );
+}
+
+/*!
+ Reads all the bytes available from the data socket and returns
+ them.
+
+ \sa get() readyRead() bytesAvailable() readBlock()
+*/
+QByteArray Q3Ftp::readAll()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pi.dtp.readAll();
+}
+
+/*!
+ Aborts the current command and deletes all scheduled commands.
+
+ If there is an unfinished command (i.e. a command for which the
+ commandStarted() signal has been emitted, but for which the
+ commandFinished() signal has not been emitted), this function
+ sends an \c ABORT command to the server. When the server replies
+ that the command is aborted, the commandFinished() signal with the
+ \c error argument set to \c true is emitted for the command. Due
+ to timing issues, it is possible that the command had already
+ finished before the abort request reached the server, in which
+ case, the commandFinished() signal is emitted with the \c error
+ argument set to \c false.
+
+ For all other commands that are affected by the abort(), no
+ signals are emitted.
+
+ If you don't start further FTP commands directly after the
+ abort(), there won't be any scheduled commands and the done()
+ signal is emitted.
+
+ \warning Some FTP servers, for example the BSD FTP daemon (version
+ 0.3), wrongly return a positive reply even when an abort has
+ occurred. For these servers the commandFinished() signal has its
+ error flag set to \c false, even though the command did not
+ complete successfully.
+
+ \sa clearPendingCommands()
+*/
+void Q3Ftp::abort()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ if ( d->pending.isEmpty() )
+ return;
+
+ clearPendingCommands();
+ d->pi.abort();
+}
+
+/*!
+ Returns the identifier of the FTP command that is being executed
+ or 0 if there is no command being executed.
+
+ \sa currentCommand()
+*/
+int Q3Ftp::currentId() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return 0;
+ return c->id;
+}
+
+/*!
+ Returns the command type of the FTP command being executed or \c
+ None if there is no command being executed.
+
+ \sa currentId()
+*/
+Q3Ftp::Command Q3Ftp::currentCommand() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return None;
+ return c->command;
+}
+
+/*!
+ Returns the QIODevice pointer that is used by the FTP command to read data
+ from or store data to. If there is no current FTP command being executed or
+ if the command does not use an IO device, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the commandFinished() signal.
+
+ \sa get() put()
+*/
+QIODevice* Q3Ftp::currentDevice() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( !c )
+ return 0;
+ if ( c->is_ba )
+ return 0;
+ return c->data.dev;
+}
+
+/*!
+ Returns true if there are any commands scheduled that have not yet
+ been executed; otherwise returns false.
+
+ The command that is being executed is \e not considered as a
+ scheduled command.
+
+ \sa clearPendingCommands() currentId() currentCommand()
+*/
+bool Q3Ftp::hasPendingCommands() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pending.count() > 1;
+}
+
+/*!
+ Deletes all pending commands from the list of scheduled commands.
+ This does not affect the command that is being executed. If you
+ want to stop this as well, use abort().
+
+ \sa hasPendingCommands() abort()
+*/
+void Q3Ftp::clearPendingCommands()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = 0;
+ if ( d->pending.count() > 0 )
+ c = d->pending.take( 0 );
+ d->pending.clear();
+ if ( c )
+ d->pending.append( c );
+}
+
+/*!
+ Returns the current state of the object. When the state changes,
+ the stateChanged() signal is emitted.
+
+ \sa State stateChanged()
+*/
+Q3Ftp::State Q3Ftp::state() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->state;
+}
+
+/*!
+ Returns the last error that occurred. This is useful to find out
+ what when wrong when receiving a commandFinished() or a done()
+ signal with the \c error argument set to \c true.
+
+ If you start a new command, the error status is reset to \c NoError.
+*/
+Q3Ftp::Error Q3Ftp::error() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->error;
+}
+
+/*!
+ Returns a human-readable description of the last error that
+ occurred. This is useful for presenting a error message to the
+ user when receiving a commandFinished() or a done() signal with
+ the \c error argument set to \c true.
+
+ The error string is often (but not always) the reply from the
+ server, so it is not always possible to translate the string. If
+ the message comes from Qt, the string has already passed through
+ tr().
+*/
+QString Q3Ftp::errorString() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->errorString;
+}
+
+int Q3Ftp::addCommand( Q3FtpCommand *cmd )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ d->pending.append( cmd );
+
+ if ( d->pending.count() == 1 )
+ // don't emit the commandStarted() signal before the id is returned
+ QTimer::singleShot( 0, this, SLOT(startNextCommand()) );
+
+ return cmd->id;
+}
+
+void Q3Ftp::startNextCommand()
+{
+ Q3FtpPrivate *d = dHelper( this );
+
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return;
+
+ d->error = NoError;
+ d->errorString = QFtp::tr( "Unknown error" );
+
+ if ( bytesAvailable() )
+ readAll(); // clear the data
+ emit commandStarted( c->id );
+
+ if ( c->command == ConnectToHost ) {
+ d->pi.connectToHost( c->rawCmds[0], c->rawCmds[1].toUInt() );
+ } else {
+ if ( c->command == Put ) {
+ if ( c->is_ba ) {
+ d->pi.dtp.setData( c->data.ba );
+ d->pi.dtp.setBytesTotal( c->data.ba->size() );
+ } else if ( c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly)) ) {
+ d->pi.dtp.setDevice( c->data.dev );
+ if ( c->data.dev->isSequentialAccess() )
+ d->pi.dtp.setBytesTotal( 0 );
+ else
+ d->pi.dtp.setBytesTotal( c->data.dev->size() );
+ }
+ } else if ( c->command == Get ) {
+ if ( !c->is_ba && c->data.dev ) {
+ d->pi.dtp.setDevice( c->data.dev );
+ }
+ } else if ( c->command == Close ) {
+ d->state = Q3Ftp::Closing;
+ emit stateChanged( d->state );
+ }
+ if ( !d->pi.sendCommands( c->rawCmds ) ) {
+ // ### error handling (this case should not happen)
+ }
+ }
+}
+
+void Q3Ftp::piFinished( const QString& )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return;
+
+ if ( c->command == Close ) {
+ // The order of in which the slots are called is arbitrary, so
+ // disconnect the SIGNAL-SIGNAL temporary to make sure that we
+ // don't get the commandFinished() signal before the stateChanged()
+ // signal.
+ if ( d->state != Q3Ftp::Unconnected ) {
+ d->close_waitForStateChange = true;
+ return;
+ }
+ }
+ emit commandFinished( c->id, false );
+
+ d->pending.removeFirst();
+ if ( d->pending.isEmpty() ) {
+ emit done( false );
+ } else {
+ startNextCommand();
+ }
+}
+
+void Q3Ftp::piError( int errorCode, const QString &text )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+
+ // non-fatal errors
+ if ( c->command==Get && d->pi.currentCommand().startsWith(QLatin1String("SIZE ")) ) {
+ d->pi.dtp.setBytesTotal( -1 );
+ return;
+ } else if ( c->command==Put && d->pi.currentCommand().startsWith(QLatin1String("ALLO ")) ) {
+ return;
+ }
+
+ d->error = (Error)errorCode;
+ switch ( currentCommand() ) {
+ case ConnectToHost:
+ d->errorString = QFtp::tr( "Connecting to host failed:\n%1" ).arg( text );
+ break;
+ case Login:
+ d->errorString = QFtp::tr( "Login failed:\n%1" ).arg( text );
+ break;
+ case List:
+ d->errorString = QFtp::tr( "Listing directory failed:\n%1" ).arg( text );
+ break;
+ case Cd:
+ d->errorString = QFtp::tr( "Changing directory failed:\n%1" ).arg( text );
+ break;
+ case Get:
+ d->errorString = QFtp::tr( "Downloading file failed:\n%1" ).arg( text );
+ break;
+ case Put:
+ d->errorString = QFtp::tr( "Uploading file failed:\n%1" ).arg( text );
+ break;
+ case Remove:
+ d->errorString = QFtp::tr( "Removing file failed:\n%1" ).arg( text );
+ break;
+ case Mkdir:
+ d->errorString = QFtp::tr( "Creating directory failed:\n%1" ).arg( text );
+ break;
+ case Rmdir:
+ d->errorString = QFtp::tr( "Removing directory failed:\n%1" ).arg( text );
+ break;
+ default:
+ d->errorString = text;
+ break;
+ }
+
+ d->pi.clearPendingCommands();
+ clearPendingCommands();
+ emit commandFinished( c->id, true );
+
+ d->pending.removeFirst();
+ if ( d->pending.isEmpty() )
+ emit done( true );
+ else
+ startNextCommand();
+}
+
+void Q3Ftp::piConnectState( int state )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ d->state = (State)state;
+ emit stateChanged( d->state );
+ if ( d->close_waitForStateChange ) {
+ d->close_waitForStateChange = false;
+ piFinished( QFtp::tr( "Connection closed" ) );
+ }
+}
+
+void Q3Ftp::piFtpReply( int code, const QString &text )
+{
+ if ( currentCommand() == RawCommand ) {
+ Q3FtpPrivate *d = dHelper( this );
+ d->pi.rawCommand = true;
+ emit rawCommandReply( code, text );
+ }
+}
+
+/*!
+ Destructor.
+*/
+Q3Ftp::~Q3Ftp()
+{
+ abort();
+ close();
+ delete_d( this );
+}
+
+/**********************************************************************
+ *
+ * Q3Ftp implementation of the Q3NetworkProtocol interface
+ *
+ *********************************************************************/
+/*! \reimp
+*/
+void Q3Ftp::operationListChildren( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? QString::fromLatin1("/") : url()->path() ) );
+ list();
+ emit start( op );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationMkDir( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ mkdir( op->arg( 0 ) );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationRemove( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? QString::fromLatin1("/") : url()->path() ) );
+ remove( Q3Url( op->arg( 0 ) ).path() );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationRename( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? QString::fromLatin1("/") : url()->path() ) );
+ rename( op->arg( 0 ), op->arg( 1 ));
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationGet( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ Q3Url u( op->arg( 0 ) );
+ get( u.path() );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationPut( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ Q3Url u( op->arg( 0 ) );
+ put( op->rawArg(1), u.path() );
+}
+
+/*! \reimp
+*/
+bool Q3Ftp::checkConnection( Q3NetworkOperation *op )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ if ( state() == Unconnected && !d->npWaitForLoginDone ) {
+ connect( this, SIGNAL(listInfo(QUrlInfo)),
+ this, SLOT(npListInfo(QUrlInfo)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(npDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(npStateChanged(int)) );
+ connect( this, SIGNAL(dataTransferProgress(int,int)),
+ this, SLOT(npDataTransferProgress(int,int)) );
+ connect( this, SIGNAL(readyRead()),
+ this, SLOT(npReadyRead()) );
+
+ d->npWaitForLoginDone = true;
+ switch ( op->operation() ) {
+ case OpGet:
+ case OpPut:
+ {
+ Q3Url u( op->arg( 0 ) );
+ connectToHost( u.host(), u.port() != -1 ? u.port() : 21 );
+ }
+ break;
+ default:
+ connectToHost( url()->host(), url()->port() != -1 ? url()->port() : 21 );
+ break;
+ }
+ QString user = url()->user().isEmpty() ? QString::fromLatin1("anonymous") : url()->user();
+ QString pass = url()->password().isEmpty() ? QString::fromLatin1("anonymous@") : url()->password();
+ login( user, pass );
+ }
+
+ if ( state() == LoggedIn )
+ return true;
+ return false;
+}
+
+/*! \reimp
+*/
+int Q3Ftp::supportedOperations() const
+{
+ return OpListChildren | OpMkDir | OpRemove | OpRename | OpGet | OpPut;
+}
+
+/*! \internal
+ Parses the string, \a buffer, which is one line of a directory
+ listing which came from the FTP server, and sets the values which
+ have been parsed to the url info object, \a info.
+*/
+void Q3Ftp::parseDir( const QString &buffer, QUrlInfo &info )
+{
+ Q3FtpDTP::parseDir( buffer, url()->user(), &info );
+}
+
+void Q3Ftp::npListInfo( const QUrlInfo & i )
+{
+ if ( url() ) {
+ QRegExp filt( url()->nameFilter(), false, true );
+ if ( i.isDir() || filt.search( i.name() ) != -1 ) {
+ emit newChild( i, operationInProgress() );
+ }
+ } else {
+ emit newChild( i, operationInProgress() );
+ }
+}
+
+void Q3Ftp::npDone( bool err )
+{
+ Q3FtpPrivate *d = dHelper( this );
+
+ bool emitFinishedSignal = false;
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( err ) {
+ op->setProtocolDetail( errorString() );
+ op->setState( StFailed );
+ if ( error() == HostNotFound ) {
+ op->setErrorCode( (int)ErrHostNotFound );
+ } else {
+ switch ( op->operation() ) {
+ case OpListChildren:
+ op->setErrorCode( (int)ErrListChildren );
+ break;
+ case OpMkDir:
+ op->setErrorCode( (int)ErrMkDir );
+ break;
+ case OpRemove:
+ op->setErrorCode( (int)ErrRemove );
+ break;
+ case OpRename:
+ op->setErrorCode( (int)ErrRename );
+ break;
+ case OpGet:
+ op->setErrorCode( (int)ErrGet );
+ break;
+ case OpPut:
+ op->setErrorCode( (int)ErrPut );
+ break;
+ }
+ }
+ emitFinishedSignal = true;
+ } else if ( !d->npWaitForLoginDone ) {
+ switch ( op->operation() ) {
+ case OpRemove:
+ emit removed( op );
+ break;
+ case OpMkDir:
+ {
+ QUrlInfo inf( op->arg( 0 ), 0, QLatin1String(""), QLatin1String(""), 0, QDateTime(),
+ QDateTime(), true, false, false, true, true, true );
+ emit newChild( inf, op );
+ emit createdDirectory( inf, op );
+ }
+ break;
+ case OpRename:
+ emit itemChanged( operationInProgress() );
+ break;
+ default:
+ break;
+ }
+ op->setState( StDone );
+ emitFinishedSignal = true;
+ }
+ }
+ d->npWaitForLoginDone = false;
+
+ if ( state() == Unconnected ) {
+ disconnect( this, SIGNAL(listInfo(QUrlInfo)),
+ this, SLOT(npListInfo(QUrlInfo)) );
+ disconnect( this, SIGNAL(done(bool)),
+ this, SLOT(npDone(bool)) );
+ disconnect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(npStateChanged(int)) );
+ disconnect( this, SIGNAL(dataTransferProgress(int,int)),
+ this, SLOT(npDataTransferProgress(int,int)) );
+ disconnect( this, SIGNAL(readyRead()),
+ this, SLOT(npReadyRead()) );
+ }
+
+ // emit the finished() signal at the very end to avoid reentrance problems
+ if ( emitFinishedSignal )
+ emit finished( op );
+}
+
+void Q3Ftp::npStateChanged( int state )
+{
+ if ( url() ) {
+ if ( state == Connecting )
+ emit connectionStateChanged( ConHostFound, QFtp::tr( "Host %1 found" ).arg( url()->host() ) );
+ else if ( state == Connected )
+ emit connectionStateChanged( ConConnected, QFtp::tr( "Connected to host %1" ).arg( url()->host() ) );
+ else if ( state == Unconnected )
+ emit connectionStateChanged( ConClosed, QFtp::tr( "Connection to %1 closed" ).arg( url()->host() ) );
+ } else {
+ if ( state == Connecting )
+ emit connectionStateChanged( ConHostFound, QFtp::tr( "Host found" ) );
+ else if ( state == Connected )
+ emit connectionStateChanged( ConConnected, QFtp::tr( "Connected to host" ) );
+ else if ( state == Unconnected )
+ emit connectionStateChanged( ConClosed, QFtp::tr( "Connection closed" ) );
+ }
+}
+
+void Q3Ftp::npDataTransferProgress( int bDone, int bTotal )
+{
+ emit Q3NetworkProtocol::dataTransferProgress( bDone, bTotal, operationInProgress() );
+}
+
+void Q3Ftp::npReadyRead()
+{
+ emit data( readAll(), operationInProgress() );
+}
+
+/*! \internal
+*/
+void Q3Ftp::hostFound()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::connected()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::closed()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataHostFound()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataConnected()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataClosed()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataReadyRead()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataBytesWritten( int )
+{
+}
+/*! \internal
+*/
+void Q3Ftp::error( int )
+{
+}
+
+QT_END_NAMESPACE
+
+#include "q3ftp.moc"
+
+#endif // QT_NO_NETWORKPROTOCOL_FTP
diff --git a/src/qt3support/network/q3ftp.h b/src/qt3support/network/q3ftp.h
new file mode 100644
index 0000000..fec1704
--- /dev/null
+++ b/src/qt3support/network/q3ftp.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3FTP_H
+#define Q3FTP_H
+
+#include <QtCore/qstring.h> // char*->QString conversion
+#include <QtNetwork/qurlinfo.h>
+#include <Qt3Support/q3networkprotocol.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+
+class Q3Socket;
+class Q3FtpCommand;
+
+class Q_COMPAT_EXPORT Q3Ftp : public Q3NetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ Q3Ftp(); // ### Qt 4.0: get rid of this overload
+ Q3Ftp( QObject *parent, const char *name=0 );
+ virtual ~Q3Ftp();
+
+ int supportedOperations() const;
+
+ // non-Q3NetworkProtocol functions:
+ enum State {
+ Unconnected,
+ HostLookup,
+ Connecting,
+ Connected,
+ LoggedIn,
+ Closing
+ };
+ enum Error {
+ NoError,
+ UnknownError,
+ HostNotFound,
+ ConnectionRefused,
+ NotConnected
+ };
+ enum Command {
+ None,
+ ConnectToHost,
+ Login,
+ Close,
+ List,
+ Cd,
+ Get,
+ Put,
+ Remove,
+ Mkdir,
+ Rmdir,
+ Rename,
+ RawCommand
+ };
+
+ int connectToHost( const QString &host, Q_UINT16 port=21 );
+ int login( const QString &user=QString(), const QString &password=QString() );
+ int close();
+ int list( const QString &dir=QString() );
+ int cd( const QString &dir );
+ int get( const QString &file, QIODevice *dev=0 );
+ int put( const QByteArray &data, const QString &file );
+ int put( QIODevice *dev, const QString &file );
+ int remove( const QString &file );
+ int mkdir( const QString &dir );
+ int rmdir( const QString &dir );
+ int rename( const QString &oldname, const QString &newname );
+
+ int rawCommand( const QString &command );
+
+ Q_ULONG bytesAvailable() const;
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ QByteArray readAll();
+
+ int currentId() const;
+ QIODevice* currentDevice() const;
+ Command currentCommand() const;
+ bool hasPendingCommands() const;
+ void clearPendingCommands();
+
+ State state() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void abort();
+
+Q_SIGNALS:
+ void stateChanged( int );
+ void listInfo( const QUrlInfo& );
+ void readyRead();
+ void dataTransferProgress( int, int );
+ void rawCommandReply( int, const QString& );
+
+ void commandStarted( int );
+ void commandFinished( int, bool );
+ void done( bool );
+
+protected:
+ void parseDir( const QString &buffer, QUrlInfo &info ); // ### Qt 4.0: delete this? (not public API)
+ void operationListChildren( Q3NetworkOperation *op );
+ void operationMkDir( Q3NetworkOperation *op );
+ void operationRemove( Q3NetworkOperation *op );
+ void operationRename( Q3NetworkOperation *op );
+ void operationGet( Q3NetworkOperation *op );
+ void operationPut( Q3NetworkOperation *op );
+
+ // ### Qt 4.0: delete these
+ // unused variables:
+ Q3Socket *commandSocket, *dataSocket;
+ bool connectionReady, passiveMode;
+ int getTotalSize, getDoneSize;
+ bool startGetOnFail;
+ int putToWrite, putWritten;
+ bool errorInListChildren;
+
+private:
+ void init();
+ int addCommand( Q3FtpCommand * );
+
+ bool checkConnection( Q3NetworkOperation *op );
+
+private Q_SLOTS:
+ void startNextCommand();
+ void piFinished( const QString& );
+ void piError( int, const QString& );
+ void piConnectState( int );
+ void piFtpReply( int, const QString& );
+
+private Q_SLOTS:
+ void npListInfo( const QUrlInfo & );
+ void npDone( bool );
+ void npStateChanged( int );
+ void npDataTransferProgress( int, int );
+ void npReadyRead();
+
+protected Q_SLOTS:
+ // ### Qt 4.0: delete these
+ void hostFound();
+ void connected();
+ void closed();
+ void dataHostFound();
+ void dataConnected();
+ void dataClosed();
+ void dataReadyRead();
+ void dataBytesWritten( int nbytes );
+ void error( int );
+};
+
+#endif // QT_NO_NETWORKPROTOCOL_FTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3FTP_H
diff --git a/src/qt3support/network/q3http.cpp b/src/qt3support/network/q3http.cpp
new file mode 100644
index 0000000..95ace0d
--- /dev/null
+++ b/src/qt3support/network/q3http.cpp
@@ -0,0 +1,2321 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include "q3http.h"
+
+#ifndef QT_NO_HTTP
+
+#include "q3socket.h"
+#include "qtextstream.h"
+#include "qmap.h"
+#include "qstring.h"
+#include "qstringlist.h"
+#include "q3cstring.h"
+#include "qbuffer.h"
+#include "q3urloperator.h"
+#include "qtimer.h"
+#include "private/q3membuf_p.h"
+#include "qevent.h"
+#include "q3url.h"
+#include "qhttp.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3HTTP_DEBUG
+
+class Q3HttpPrivate
+{
+public:
+ Q3HttpPrivate() :
+ state( Q3Http::Unconnected ),
+ error( Q3Http::NoError ),
+ hostname( QString() ),
+ port( 0 ),
+ toDevice( 0 ),
+ postDevice( 0 ),
+ bytesDone( 0 ),
+ chunkedSize( -1 ),
+ idleTimer( 0 )
+ {
+ pending.setAutoDelete( true );
+ }
+
+ Q3Socket socket;
+ Q3PtrList<Q3HttpRequest> pending;
+
+ Q3Http::State state;
+ Q3Http::Error error;
+ QString errorString;
+
+ QString hostname;
+ Q_UINT16 port;
+
+ QByteArray buffer;
+ QIODevice* toDevice;
+ QIODevice* postDevice;
+
+ uint bytesDone;
+ uint bytesTotal;
+ Q_LONG chunkedSize;
+
+ Q3HttpRequestHeader header;
+
+ bool readHeader;
+ QString headerStr;
+ Q3HttpResponseHeader response;
+
+ int idleTimer;
+
+ Q3Membuf rba;
+};
+
+class Q3HttpRequest
+{
+public:
+ Q3HttpRequest()
+ {
+ id = ++idCounter;
+ }
+ virtual ~Q3HttpRequest()
+ { }
+
+ virtual void start( Q3Http * ) = 0;
+ virtual bool hasRequestHeader();
+ virtual Q3HttpRequestHeader requestHeader();
+
+ virtual QIODevice* sourceDevice() = 0;
+ virtual QIODevice* destinationDevice() = 0;
+
+ int id;
+
+private:
+ static int idCounter;
+};
+
+int Q3HttpRequest::idCounter = 0;
+
+bool Q3HttpRequest::hasRequestHeader()
+{
+ return false;
+}
+
+Q3HttpRequestHeader Q3HttpRequest::requestHeader()
+{
+ return Q3HttpRequestHeader();
+}
+
+/****************************************************
+ *
+ * Q3HttpNormalRequest
+ *
+ ****************************************************/
+
+class Q3HttpNormalRequest : public Q3HttpRequest
+{
+public:
+ Q3HttpNormalRequest( const Q3HttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
+ header(h), to(t)
+ {
+ is_ba = false;
+ data.dev = d;
+ }
+
+ Q3HttpNormalRequest( const Q3HttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
+ header(h), to(t)
+ {
+ is_ba = true;
+ data.ba = d;
+ }
+
+ ~Q3HttpNormalRequest()
+ {
+ if ( is_ba )
+ delete data.ba;
+ }
+
+ void start( Q3Http * );
+ bool hasRequestHeader();
+ Q3HttpRequestHeader requestHeader();
+
+ QIODevice* sourceDevice();
+ QIODevice* destinationDevice();
+
+protected:
+ Q3HttpRequestHeader header;
+
+private:
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+ QIODevice *to;
+};
+
+void Q3HttpNormalRequest::start( Q3Http *http )
+{
+ http->d->header = header;
+
+ if ( is_ba ) {
+ http->d->buffer = *data.ba;
+ if ( http->d->buffer.size() > 0 )
+ http->d->header.setContentLength( http->d->buffer.size() );
+
+ http->d->postDevice = 0;
+ } else {
+ http->d->buffer = QByteArray();
+
+ if ( data.dev && ( data.dev->isOpen() || data.dev->open(IO_ReadOnly) ) ) {
+ http->d->postDevice = data.dev;
+ if ( http->d->postDevice->size() > 0 )
+ http->d->header.setContentLength( http->d->postDevice->size() );
+ } else {
+ http->d->postDevice = 0;
+ }
+ }
+
+ if ( to && ( to->isOpen() || to->open(IO_WriteOnly) ) )
+ http->d->toDevice = to;
+ else
+ http->d->toDevice = 0;
+
+ http->sendRequest();
+}
+
+bool Q3HttpNormalRequest::hasRequestHeader()
+{
+ return true;
+}
+
+Q3HttpRequestHeader Q3HttpNormalRequest::requestHeader()
+{
+ return header;
+}
+
+QIODevice* Q3HttpNormalRequest::sourceDevice()
+{
+ if ( is_ba )
+ return 0;
+ return data.dev;
+}
+
+QIODevice* Q3HttpNormalRequest::destinationDevice()
+{
+ return to;
+}
+
+/****************************************************
+ *
+ * Q3HttpPGHRequest
+ * (like a Q3HttpNormalRequest, but for the convenience
+ * functions put(), get() and head() -- i.e. set the
+ * host header field correctly before sending the
+ * request)
+ *
+ ****************************************************/
+
+class Q3HttpPGHRequest : public Q3HttpNormalRequest
+{
+public:
+ Q3HttpPGHRequest( const Q3HttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
+ Q3HttpNormalRequest( h, d, t )
+ { }
+
+ Q3HttpPGHRequest( const Q3HttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
+ Q3HttpNormalRequest( h, d, t )
+ { }
+
+ ~Q3HttpPGHRequest()
+ { }
+
+ void start( Q3Http * );
+};
+
+void Q3HttpPGHRequest::start( Q3Http *http )
+{
+ header.setValue( QLatin1String("Host"), http->d->hostname );
+ Q3HttpNormalRequest::start( http );
+}
+
+/****************************************************
+ *
+ * Q3HttpSetHostRequest
+ *
+ ****************************************************/
+
+class Q3HttpSetHostRequest : public Q3HttpRequest
+{
+public:
+ Q3HttpSetHostRequest( const QString &h, Q_UINT16 p ) :
+ hostname(h), port(p)
+ { }
+
+ void start( Q3Http * );
+
+ QIODevice* sourceDevice()
+ { return 0; }
+ QIODevice* destinationDevice()
+ { return 0; }
+
+private:
+ QString hostname;
+ Q_UINT16 port;
+};
+
+void Q3HttpSetHostRequest::start( Q3Http *http )
+{
+ http->d->hostname = hostname;
+ http->d->port = port;
+ http->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * Q3HttpCloseRequest
+ *
+ ****************************************************/
+
+class Q3HttpCloseRequest : public Q3HttpRequest
+{
+public:
+ Q3HttpCloseRequest()
+ { }
+ void start( Q3Http * );
+
+ QIODevice* sourceDevice()
+ { return 0; }
+ QIODevice* destinationDevice()
+ { return 0; }
+};
+
+void Q3HttpCloseRequest::start( Q3Http *http )
+{
+ http->close();
+}
+
+/****************************************************
+ *
+ * Q3HttpHeader
+ *
+ ****************************************************/
+
+/*!
+ \class Q3HttpHeader
+ \brief The Q3HttpHeader class contains header information for HTTP.
+
+ \compat
+
+ In most cases you should use the more specialized derivatives of
+ this class, Q3HttpResponseHeader and Q3HttpRequestHeader, rather
+ than directly using Q3HttpHeader.
+
+ Q3HttpHeader provides the HTTP header fields. A HTTP header field
+ consists of a name followed by a colon, a single space, and the
+ field value. (See RFC 1945.) Field names are case-insensitive. A
+ typical header field looks like this:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 0
+
+ In the API the header field name is called the "key" and the
+ content is called the "value". You can get and set a header
+ field's value by using its key with value() and setValue(), e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 1
+
+ Some fields are so common that getters and setters are provided
+ for them as a convenient alternative to using \l value() and
+ \l setValue(), e.g. contentLength() and contentType(),
+ setContentLength() and setContentType().
+
+ Each header key has a \e single value associated with it. If you
+ set the value for a key which already exists the previous value
+ will be discarded.
+
+ \sa Q3HttpRequestHeader Q3HttpResponseHeader
+*/
+
+/*!
+ \fn int Q3HttpHeader::majorVersion() const
+
+ Returns the major protocol-version of the HTTP header.
+*/
+
+/*!
+ \fn int Q3HttpHeader::minorVersion() const
+
+ Returns the minor protocol-version of the HTTP header.
+*/
+
+/*!
+ Constructs an empty HTTP header.
+*/
+Q3HttpHeader::Q3HttpHeader()
+ : valid( true )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+Q3HttpHeader::Q3HttpHeader( const Q3HttpHeader& header )
+ : valid( header.valid )
+{
+ values = header.values;
+}
+
+/*!
+ Constructs a HTTP header for \a str.
+
+ This constructor parses the string \a str for header fields and
+ adds this information. The \a str should consist of one or more
+ "\r\n" delimited lines; each of these lines should have the format
+ key, colon, space, value.
+*/
+Q3HttpHeader::Q3HttpHeader( const QString& str )
+ : valid( true )
+{
+ parse( str );
+}
+
+/*!
+ Destructor.
+*/
+Q3HttpHeader::~Q3HttpHeader()
+{
+}
+
+/*!
+ Assigns \a h and returns a reference to this http header.
+*/
+Q3HttpHeader& Q3HttpHeader::operator=( const Q3HttpHeader& h )
+{
+ values = h.values;
+ valid = h.valid;
+ return *this;
+}
+
+/*!
+ Returns true if the HTTP header is valid; otherwise returns false.
+
+ A Q3HttpHeader is invalid if it was created by parsing a malformed string.
+*/
+bool Q3HttpHeader::isValid() const
+{
+ return valid;
+}
+
+/*! \internal
+ Parses the HTTP header string \a str for header fields and adds
+ the keys/values it finds. If the string is not parsed successfully
+ the Q3HttpHeader becomes \link isValid() invalid\endlink.
+
+ Returns true if \a str was successfully parsed; otherwise returns false.
+
+ \sa toString()
+*/
+bool Q3HttpHeader::parse( const QString& str )
+{
+ QStringList lst;
+ int pos = str.find( QLatin1Char('\n') );
+ if ( pos > 0 && str.at( pos - 1 ) == QLatin1Char('\r') )
+ lst = QStringList::split( QLatin1String("\r\n"), str.stripWhiteSpace(), false );
+ else
+ lst = QStringList::split( QLatin1String("\n"), str.stripWhiteSpace(), false );
+
+ if ( lst.isEmpty() )
+ return true;
+
+ QStringList lines;
+ QStringList::Iterator it = lst.begin();
+ for( ; it != lst.end(); ++it ) {
+ if ( !(*it).isEmpty() ) {
+ if ( (*it)[0].isSpace() ) {
+ if ( !lines.isEmpty() ) {
+ lines.last() += QLatin1Char(' ');
+ lines.last() += (*it).stripWhiteSpace();
+ }
+ } else {
+ lines.append( (*it) );
+ }
+ }
+ }
+
+ int number = 0;
+ it = lines.begin();
+ for( ; it != lines.end(); ++it ) {
+ if ( !parseLine( *it, number++ ) ) {
+ valid = false;
+ return false;
+ }
+ }
+ return true;
+}
+
+/*! \internal
+*/
+void Q3HttpHeader::setValid( bool v )
+{
+ valid = v;
+}
+
+/*!
+ Returns the value for the entry with the given \a key. If no entry
+ has this \a key, an empty string is returned.
+
+ \sa setValue() removeValue() hasKey() keys()
+*/
+QString Q3HttpHeader::value( const QString& key ) const
+{
+ return values[ key.lower() ];
+}
+
+/*!
+ Returns a list of the keys in the HTTP header.
+
+ \sa hasKey()
+*/
+QStringList Q3HttpHeader::keys() const
+{
+ return values.keys();
+}
+
+/*!
+ Returns true if the HTTP header has an entry with the given \a
+ key; otherwise returns false.
+
+ \sa value() setValue() keys()
+*/
+bool Q3HttpHeader::hasKey( const QString& key ) const
+{
+ return values.contains( key.lower() );
+}
+
+/*!
+ Sets the value of the entry with the \a key to \a value.
+
+ If no entry with \a key exists, a new entry with the given \a key
+ and \a value is created. If an entry with the \a key already
+ exists, its value is discarded and replaced with the given \a
+ value.
+
+ \sa value() hasKey() removeValue()
+*/
+void Q3HttpHeader::setValue( const QString& key, const QString& value )
+{
+ values[ key.lower() ] = value;
+}
+
+/*!
+ Removes the entry with the key \a key from the HTTP header.
+
+ \sa value() setValue()
+*/
+void Q3HttpHeader::removeValue( const QString& key )
+{
+ values.remove( key.lower() );
+}
+
+/*! \internal
+ Parses the single HTTP header line \a line which has the format
+ key, colon, space, value, and adds key/value to the headers. The
+ linenumber is \a number. Returns true if the line was successfully
+ parsed and the key/value added; otherwise returns false.
+
+ \sa parse()
+*/
+bool Q3HttpHeader::parseLine( const QString& line, int )
+{
+ int i = line.find( QLatin1Char(':') );
+ if ( i == -1 )
+ return false;
+
+ values.insert( line.left( i ).stripWhiteSpace().lower(), line.mid( i + 1 ).stripWhiteSpace() );
+
+ return true;
+}
+
+/*!
+ Returns a string representation of the HTTP header.
+
+ The string is suitable for use by the constructor that takes a
+ QString. It consists of lines with the format: key, colon, space,
+ value, "\r\n".
+*/
+QString Q3HttpHeader::toString() const
+{
+ if ( !isValid() )
+ return QLatin1String("");
+
+ QString ret = QLatin1String("");
+
+ QMap<QString,QString>::ConstIterator it = values.begin();
+ for( ; it != values.end(); ++it )
+ ret += it.key() + QLatin1String(": ") + it.data() + QLatin1String("\r\n");
+
+ return ret;
+}
+
+/*!
+ Returns true if the header has an entry for the special HTTP
+ header field \c content-length; otherwise returns false.
+
+ \sa contentLength() setContentLength()
+*/
+bool Q3HttpHeader::hasContentLength() const
+{
+ return hasKey( QLatin1String("content-length") );
+}
+
+/*!
+ Returns the value of the special HTTP header field \c
+ content-length.
+
+ \sa setContentLength() hasContentLength()
+*/
+uint Q3HttpHeader::contentLength() const
+{
+ return values[ QLatin1String("content-length") ].toUInt();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-length
+ to \a len.
+
+ \sa contentLength() hasContentLength()
+*/
+void Q3HttpHeader::setContentLength( int len )
+{
+ values[ QLatin1String("content-length") ] = QString::number( len );
+}
+
+/*!
+ Returns true if the header has an entry for the special HTTP
+ header field \c content-type; otherwise returns false.
+
+ \sa contentType() setContentType()
+*/
+bool Q3HttpHeader::hasContentType() const
+{
+ return hasKey( QLatin1String("content-type") );
+}
+
+/*!
+ Returns the value of the special HTTP header field \c content-type.
+
+ \sa setContentType() hasContentType()
+*/
+QString Q3HttpHeader::contentType() const
+{
+ QString type = values[ QLatin1String("content-type") ];
+ if ( type.isEmpty() )
+ return QString();
+
+ int pos = type.find( QLatin1Char(';') );
+ if ( pos == -1 )
+ return type;
+
+ return type.left( pos ).stripWhiteSpace();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-type to
+ \a type.
+
+ \sa contentType() hasContentType()
+*/
+void Q3HttpHeader::setContentType( const QString& type )
+{
+ values[ QLatin1String("content-type") ] = type;
+}
+
+/****************************************************
+ *
+ * Q3HttpResponseHeader
+ *
+ ****************************************************/
+
+/*!
+ \class Q3HttpResponseHeader
+ \brief The Q3HttpResponseHeader class contains response header information for HTTP.
+
+ \compat
+
+ This class is used by the Q3Http class to report the header
+ information that the client received from the server.
+
+ HTTP responses have a status code that indicates the status of the
+ response. This code is a 3-digit integer result code (for details
+ see to RFC 1945). In addition to the status code, you can also
+ specify a human-readable text that describes the reason for the
+ code ("reason phrase"). This class allows you to get the status
+ code and the reason phrase.
+
+ \sa Q3HttpRequestHeader Q3Http
+*/
+
+/*!
+ Constructs an empty HTTP response header.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader()
+{
+ setValid( false );
+}
+
+/*!
+ Constructs a HTTP response header with the status code \a code,
+ the reason phrase \a text and the protocol-version \a majorVer and
+ \a minorVer.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader( int code, const QString& text, int majorVer, int minorVer )
+ : Q3HttpHeader(), statCode( code ), reasonPhr( text ), majVer( majorVer ), minVer( minorVer )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader( const Q3HttpResponseHeader& header )
+ : Q3HttpHeader( header ), statCode( header.statCode ), reasonPhr( header.reasonPhr ), majVer( header.majVer ), minVer( header.minVer )
+{
+}
+
+/*!
+ Constructs a HTTP response header from the string \a str. The
+ string is parsed and the information is set. The \a str should
+ consist of one or more "\r\n" delimited lines; the first line should be the
+ status-line (format: HTTP-version, space, status-code, space,
+ reason-phrase); each of remaining lines should have the format key, colon,
+ space, value.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader( const QString& str )
+ : Q3HttpHeader()
+{
+ parse( str );
+}
+
+/*!
+ Sets the status code to \a code, the reason phrase to \a text and
+ the protocol-version to \a majorVer and \a minorVer.
+
+ \sa statusCode() reasonPhrase() majorVersion() minorVersion()
+*/
+void Q3HttpResponseHeader::setStatusLine( int code, const QString& text, int majorVer, int minorVer )
+{
+ setValid( true );
+ statCode = code;
+ reasonPhr = text;
+ majVer = majorVer;
+ minVer = minorVer;
+}
+
+/*!
+ Returns the status code of the HTTP response header.
+
+ \sa reasonPhrase() majorVersion() minorVersion()
+*/
+int Q3HttpResponseHeader::statusCode() const
+{
+ return statCode;
+}
+
+/*!
+ Returns the reason phrase of the HTTP response header.
+
+ \sa statusCode() majorVersion() minorVersion()
+*/
+QString Q3HttpResponseHeader::reasonPhrase() const
+{
+ return reasonPhr;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP response header.
+
+ \sa minorVersion() statusCode() reasonPhrase()
+*/
+int Q3HttpResponseHeader::majorVersion() const
+{
+ return majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP response header.
+
+ \sa majorVersion() statusCode() reasonPhrase()
+*/
+int Q3HttpResponseHeader::minorVersion() const
+{
+ return minVer;
+}
+
+/*! \internal
+*/
+bool Q3HttpResponseHeader::parseLine( const QString& line, int number )
+{
+ if ( number != 0 )
+ return Q3HttpHeader::parseLine( line, number );
+
+ QString l = line.simplifyWhiteSpace();
+ if ( l.length() < 10 )
+ return false;
+
+ if ( l.left( 5 ) == QLatin1String("HTTP/") && l[5].isDigit() && l[6] == QLatin1Char('.') &&
+ l[7].isDigit() && l[8] == QLatin1Char(' ') && l[9].isDigit() ) {
+ majVer = l[5].latin1() - '0';
+ minVer = l[7].latin1() - '0';
+
+ int pos = l.find( QLatin1Char(' '), 9 );
+ if ( pos != -1 ) {
+ reasonPhr = l.mid( pos + 1 );
+ statCode = l.mid( 9, pos - 9 ).toInt();
+ } else {
+ statCode = l.mid( 9 ).toInt();
+ reasonPhr.clear();
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+/*! \reimp
+*/
+QString Q3HttpResponseHeader::toString() const
+{
+ QString ret( QLatin1String("HTTP/%1.%2 %3 %4\r\n%5\r\n") );
+ return ret.arg( majVer ).arg ( minVer ).arg( statCode ).arg( reasonPhr ).arg( Q3HttpHeader::toString() );
+}
+
+/****************************************************
+ *
+ * Q3HttpRequestHeader
+ *
+ ****************************************************/
+
+/*!
+ \class Q3HttpRequestHeader
+ \brief The Q3HttpRequestHeader class contains request header information for
+ HTTP.
+
+ \compat
+
+ This class is used in the Q3Http class to report the header
+ information if the client requests something from the server.
+
+ HTTP requests have a method which describes the request's action.
+ The most common requests are "GET" and "POST". In addition to the
+ request method the header also includes a request-URI to specify
+ the location for the method to use.
+
+ The method, request-URI and protocol-version can be set using a
+ constructor or later using setRequest(). The values can be
+ obtained using method(), path(), majorVersion() and
+ minorVersion().
+
+ This class is a Q3HttpHeader subclass so that class's functions,
+ e.g. \link Q3HttpHeader::setValue() setValue()\endlink, \link
+ Q3HttpHeader::value() value()\endlink, etc. are also available.
+
+ \sa Q3HttpResponseHeader Q3Http
+*/
+
+/*!
+ Constructs an empty HTTP request header.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader()
+ : Q3HttpHeader()
+{
+ setValid( false );
+}
+
+/*!
+ Constructs a HTTP request header for the method \a method, the
+ request-URI \a path and the protocol-version \a majorVer and \a minorVer.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader( const QString& method, const QString& path, int majorVer, int minorVer )
+ : Q3HttpHeader(), m( method ), p( path ), majVer( majorVer ), minVer( minorVer )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader( const Q3HttpRequestHeader& header )
+ : Q3HttpHeader( header ), m( header.m ), p( header.p ), majVer( header.majVer ), minVer( header.minVer )
+{
+}
+
+/*!
+ Constructs a HTTP request header from the string \a str. The \a
+ str should consist of one or more "\r\n" delimited lines; the first line
+ should be the request-line (format: method, space, request-URI, space
+ HTTP-version); each of the remaining lines should have the format key,
+ colon, space, value.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader( const QString& str )
+ : Q3HttpHeader()
+{
+ parse( str );
+}
+
+/*!
+ This function sets the request method to \a method, the
+ request-URI to \a path and the protocol-version to \a majorVer and
+ \a minorVer.
+
+ \sa method() path() majorVersion() minorVersion()
+*/
+void Q3HttpRequestHeader::setRequest( const QString& method, const QString& path, int majorVer, int minorVer )
+{
+ setValid( true );
+ m = method;
+ p = path;
+ majVer = majorVer;
+ minVer = minorVer;
+}
+
+/*!
+ Returns the method of the HTTP request header.
+
+ \sa path() majorVersion() minorVersion() setRequest()
+*/
+QString Q3HttpRequestHeader::method() const
+{
+ return m;
+}
+
+/*!
+ Returns the request-URI of the HTTP request header.
+
+ \sa method() majorVersion() minorVersion() setRequest()
+*/
+QString Q3HttpRequestHeader::path() const
+{
+ return p;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP request header.
+
+ \sa minorVersion() method() path() setRequest()
+*/
+int Q3HttpRequestHeader::majorVersion() const
+{
+ return majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP request header.
+
+ \sa majorVersion() method() path() setRequest()
+*/
+int Q3HttpRequestHeader::minorVersion() const
+{
+ return minVer;
+}
+
+/*! \internal
+*/
+bool Q3HttpRequestHeader::parseLine( const QString& line, int number )
+{
+ if ( number != 0 )
+ return Q3HttpHeader::parseLine( line, number );
+
+ QStringList lst = QStringList::split( QLatin1String(" "), line.simplifyWhiteSpace() );
+ if ( lst.count() > 0 ) {
+ m = lst[0];
+ if ( lst.count() > 1 ) {
+ p = lst[1];
+ if ( lst.count() > 2 ) {
+ QString v = lst[2];
+ if ( v.length() >= 8 && v.left( 5 ) == QLatin1String("HTTP/") &&
+ v[5].isDigit() && v[6] == QLatin1Char('.') && v[7].isDigit() ) {
+ majVer = v[5].latin1() - '0';
+ minVer = v[7].latin1() - '0';
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/*! \reimp
+*/
+QString Q3HttpRequestHeader::toString() const
+{
+ QString first( QLatin1String("%1 %2"));
+ QString last(QLatin1String(" HTTP/%3.%4\r\n%5\r\n") );
+ return first.arg( m ).arg( p ) +
+ last.arg( majVer ).arg( minVer ).arg( Q3HttpHeader::toString());
+}
+
+
+/****************************************************
+ *
+ * Q3Http
+ *
+ ****************************************************/
+/*!
+ \class Q3Http
+ \brief The Q3Http class provides an implementation of the HTTP protocol.
+
+ \compat
+
+ This class provides two different interfaces: one is the
+ Q3NetworkProtocol interface that allows you to use HTTP through the
+ QUrlOperator abstraction. The other is a direct interface to HTTP
+ that allows you to have more control over the requests and that
+ allows you to access the response header fields.
+
+ Don't mix the two interfaces, since the behavior is not
+ well-defined.
+
+ If you want to use Q3Http with the Q3NetworkProtocol interface, you
+ do not use it directly, but rather through a QUrlOperator, for
+ example:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 2
+
+ This code will only work if the Q3Http class is registered; to
+ register the class, you must call q3InitNetworkProtocols() before
+ using a QUrlOperator with HTTP.
+
+ The Q3NetworkProtocol interface for HTTP only supports the
+ operations operationGet() and operationPut(), i.e.
+ QUrlOperator::get() and QUrlOperator::put(), if you use it with a
+ QUrlOperator.
+
+ The rest of this descrption describes the direct interface to
+ HTTP.
+
+ The class works asynchronously, so there are no blocking
+ functions. If an operation cannot be executed immediately, the
+ function will still return straight away and the operation will be
+ scheduled for later execution. The results of scheduled operations
+ are reported via signals. This approach depends on the event loop
+ being in operation.
+
+ The operations that can be scheduled (they are called "requests"
+ in the rest of the documentation) are the following: setHost(),
+ get(), post(), head() and request().
+
+ All of these requests return a unique identifier that allows you
+ to keep track of the request that is currently executed. When the
+ execution of a request starts, the requestStarted() signal with
+ the identifier is emitted and when the request is finished, the
+ requestFinished() signal is emitted with the identifier and a bool
+ that indicates if the request finished with an error.
+
+ To make an HTTP request you must set up suitable HTTP headers. The
+ following example demonstrates, how to request the main HTML page
+ from the Qt website (i.e. the URL \c http://qt.nokia.com/index.html):
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 3
+
+ For the common HTTP requests \c GET, \c POST and \c HEAD, Q3Http
+ provides the convenience functions get(), post() and head(). They
+ already use a reasonable header and if you don't have to set
+ special header fields, they are easier to use. The above example
+ can also be written as:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 4
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 5
+
+ The dataSendProgress() and dataReadProgress() signals in the above
+ example are useful if you want to show a \link QProgressBar
+ progress bar\endlink to inform the user about the progress of the
+ download. The second argument is the total size of data. In
+ certain cases it is not possible to know the total amount in
+ advance, in which case the second argument is 0. (If you connect
+ to a QProgressBar a total of 0 results in a busy indicator.)
+
+ When the response header is read, it is reported with the
+ responseHeaderReceived() signal.
+
+ The readyRead() signal tells you that there is data ready to be
+ read. The amount of data can then be queried with the
+ bytesAvailable() function and it can be read with the readBlock()
+ or readAll() functions.
+
+ If an error occurs during the execution of one of the commands in
+ a sequence of commands, all the pending commands (i.e. scheduled,
+ but not yet executed commands) are cleared and no signals are
+ emitted for them.
+
+ For example, if you have the following sequence of reqeusts
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 6
+
+ and the get() request fails because the host lookup fails, then
+ the post() request is never executed and the signals would look
+ like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 7
+
+ You can then get details about the error with the error() and
+ errorString() functions. Note that only unexpected behaviour, like
+ network failure is considered as an error. If the server response
+ contains an error status, like a 404 response, this is reported as
+ a normal response case. So you should always check the \link
+ Q3HttpResponseHeader::statusCode() status code \endlink of the
+ response header.
+
+ The functions currentId() and currentRequest() provide more
+ information about the currently executing request.
+
+ The functions hasPendingRequests() and clearPendingRequests()
+ allow you to query and clear the list of pending requests.
+
+ \sa Q3NetworkProtocol, Q3UrlOperator, Q3Ftp
+*/
+
+/*!
+ Constructs a Q3Http object.
+*/
+Q3Http::Q3Http()
+{
+ init();
+}
+
+/*!
+ Constructs a Q3Http object. The parameters \a parent and \a name
+ are passed on to the QObject constructor.
+*/
+Q3Http::Q3Http( QObject* parent, const char* name )
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+}
+
+/*!
+ Constructs a Q3Http object. Subsequent requests are done by
+ connecting to the server \a hostname on port \a port. The
+ parameters \a parent and \a name are passed on to the QObject
+ constructor.
+
+ \sa setHost()
+*/
+Q3Http::Q3Http( const QString &hostname, Q_UINT16 port, QObject* parent, const char* name )
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+
+ d->hostname = hostname;
+ d->port = port;
+}
+
+void Q3Http::init()
+{
+ bytesRead = 0;
+ d = new Q3HttpPrivate;
+ d->errorString = QHttp::tr( "Unknown error" );
+
+ connect( &d->socket, SIGNAL(connected()),
+ this, SLOT(slotConnected()) );
+ connect( &d->socket, SIGNAL(connectionClosed()),
+ this, SLOT(slotClosed()) );
+ connect( &d->socket, SIGNAL(delayedCloseFinished()),
+ this, SLOT(slotClosed()) );
+ connect( &d->socket, SIGNAL(readyRead()),
+ this, SLOT(slotReadyRead()) );
+ connect( &d->socket, SIGNAL(error(int)),
+ this, SLOT(slotError(int)) );
+ connect( &d->socket, SIGNAL(bytesWritten(int)),
+ this, SLOT(slotBytesWritten(int)) );
+
+ d->idleTimer = startTimer( 0 );
+}
+
+/*!
+ Destroys the Q3Http object. If there is an open connection, it is
+ closed.
+*/
+Q3Http::~Q3Http()
+{
+ abort();
+ delete d;
+}
+
+/*!
+ \enum Q3Http::State
+
+ This enum is used to specify the state the client is in:
+
+ \value Unconnected There is no connection to the host.
+ \value HostLookup A host name lookup is in progress.
+ \value Connecting An attempt to connect to the host is in progress.
+ \value Sending The client is sending its request to the server.
+ \value Reading The client's request has been sent and the client
+ is reading the server's response.
+ \value Connected The connection to the host is open, but the client is
+ neither sending a request, nor waiting for a response.
+ \value Closing The connection is closing down, but is not yet
+ closed. (The state will be \c Unconnected when the connection is
+ closed.)
+
+ \sa stateChanged() state()
+*/
+
+/*! \enum Q3Http::Error
+
+ This enum identifies the error that occurred.
+
+ \value NoError No error occurred.
+ \value HostNotFound The host name lookup failed.
+ \value ConnectionRefused The server refused the connection.
+ \value UnexpectedClose The server closed the connection unexpectedly.
+ \value InvalidResponseHeader The server sent an invalid response header.
+ \value WrongContentLength The client could not read the content correctly
+ because an error with respect to the content length occurred.
+ \value Aborted The request was aborted with abort().
+ \value UnknownError An error other than those specified above
+ occurred.
+
+ \sa error()
+*/
+
+/*!
+ \fn void Q3Http::stateChanged( int state )
+
+ This signal is emitted when the state of the Q3Http object changes.
+ The argument \a state is the new state of the connection; it is
+ one of the \l State values.
+
+ This usually happens when a request is started, but it can also
+ happen when the server closes the connection or when a call to
+ closeConnection() succeeded.
+
+ \sa get() post() head() request() closeConnection() state() State
+*/
+
+/*!
+ \fn void Q3Http::responseHeaderReceived( const Q3HttpResponseHeader& resp )
+
+ This signal is emitted when the HTTP header of a server response
+ is available. The header is passed in \a resp.
+
+ \sa get() post() head() request() readyRead()
+*/
+
+/*!
+ \fn void Q3Http::readyRead( const Q3HttpResponseHeader& resp )
+
+ This signal is emitted when there is new response data to read.
+
+ If you specified a device in the request where the data should be
+ written to, then this signal is \e not emitted; instead the data
+ is written directly to the device.
+
+ The response header is passed in \a resp.
+
+ You can read the data with the readAll() or readBlock() functions
+
+ This signal is useful if you want to process the data in chunks as
+ soon as it becomes available. If you are only interested in the
+ complete data, just connect to the requestFinished() signal and
+ read the data then instead.
+
+ \sa get() post() request() readAll() readBlock() bytesAvailable()
+*/
+
+/*!
+ \fn void Q3Http::dataSendProgress( int done, int total )
+
+ This signal is emitted when this object sends data to a HTTP
+ server to inform it about the progress of the upload.
+
+ \a done is the amount of data that has already arrived and \a
+ total is the total amount of data. It is possible that the total
+ amount of data that should be transferred cannot be determined, in
+ which case \a total is 0.(If you connect to a QProgressBar, the
+ progress bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa dataReadProgress() post() request() QProgressBar::setValue()
+*/
+
+/*!
+ \fn void Q3Http::dataReadProgress( int done, int total )
+
+ This signal is emitted when this object reads data from a HTTP
+ server to indicate the current progress of the download.
+
+ \a done is the amount of data that has already arrived and \a
+ total is the total amount of data. It is possible that the total
+ amount of data that should be transferred cannot be determined, in
+ which case \a total is 0.(If you connect to a QProgressBar, the
+ progress bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa dataSendProgress() get() post() request() QProgressBar::setValue()
+*/
+
+/*!
+ \fn void Q3Http::requestStarted( int id )
+
+ This signal is emitted when processing the request identified by
+ \a id starts.
+
+ \sa requestFinished() done()
+*/
+
+/*!
+ \fn void Q3Http::requestFinished( int id, bool error )
+
+ This signal is emitted when processing the request identified by
+ \a id has finished. \a error is true if an error occurred during
+ the processing; otherwise \a error is false.
+
+ \sa requestStarted() done() error() errorString()
+*/
+
+/*!
+ \fn void Q3Http::done( bool error )
+
+ This signal is emitted when the last pending request has finished;
+ (it is emitted after the last request's requestFinished() signal).
+ \a error is true if an error occurred during the processing;
+ otherwise \a error is false.
+
+ \sa requestFinished() error() errorString()
+*/
+
+/*!
+ Aborts the current request and deletes all scheduled requests.
+
+ For the current request, the requestFinished() signal with the \c
+ error argument \c true is emitted. For all other requests that are
+ affected by the abort(), no signals are emitted.
+
+ Since this slot also deletes the scheduled requests, there are no
+ requests left and the done() signal is emitted (with the \c error
+ argument \c true).
+
+ \sa clearPendingRequests()
+*/
+void Q3Http::abort()
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ finishedWithError( QHttp::tr("Request aborted"), Aborted );
+ clearPendingRequests();
+ d->socket.clearPendingData();
+ close();
+}
+
+/*!
+ Returns the number of bytes that can be read from the response
+ content at the moment.
+
+ \sa get() post() request() readyRead() readBlock() readAll()
+*/
+Q_ULONG Q3Http::bytesAvailable() const
+{
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::bytesAvailable(): %d bytes", (int)d->rba.size() );
+#endif
+ return d->rba.size();
+}
+
+/*!
+ Reads \a maxlen bytes from the response content into \a data and
+ returns the number of bytes read. Returns -1 if an error occurred.
+
+ \sa get() post() request() readyRead() bytesAvailable() readAll()
+*/
+Q_LONG Q3Http::readBlock( char *data, Q_ULONG maxlen )
+{
+ if ( data == 0 && maxlen != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3Http::readBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( maxlen >= (Q_ULONG)d->rba.size() )
+ maxlen = d->rba.size();
+ d->rba.consumeBytes( maxlen, data );
+
+ d->bytesDone += maxlen;
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::readBlock(): read %d bytes (%d bytes done)", (int)maxlen, d->bytesDone );
+#endif
+ return maxlen;
+}
+
+/*!
+ Reads all the bytes from the response content and returns them.
+
+ \sa get() post() request() readyRead() bytesAvailable() readBlock()
+*/
+QByteArray Q3Http::readAll()
+{
+ Q_ULONG avail = bytesAvailable();
+ QByteArray tmp( avail );
+ Q_LONG read = readBlock( tmp.data(), avail );
+ tmp.resize( read );
+ return tmp;
+}
+
+/*!
+ Returns the identifier of the HTTP request being executed or 0 if
+ there is no request being executed (i.e. they've all finished).
+
+ \sa currentRequest()
+*/
+int Q3Http::currentId() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return 0;
+ return r->id;
+}
+
+/*!
+ Returns the request header of the HTTP request being executed. If
+ the request is one issued by setHost() or closeConnection(), it
+ returns an invalid request header, i.e.
+ Q3HttpRequestHeader::isValid() returns false.
+
+ \sa currentId()
+*/
+Q3HttpRequestHeader Q3Http::currentRequest() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r != 0 && r->hasRequestHeader() )
+ return r->requestHeader();
+ return Q3HttpRequestHeader();
+}
+
+/*!
+ Returns the QIODevice pointer that is used as the data source of the HTTP
+ request being executed. If there is no current request or if the request
+ does not use an IO device as the data source, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa currentDestinationDevice() post() request()
+*/
+QIODevice* Q3Http::currentSourceDevice() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( !r )
+ return 0;
+ return r->sourceDevice();
+}
+
+/*!
+ Returns the QIODevice pointer that is used as to store the data of the HTTP
+ request being executed. If there is no current request or if the request
+ does not store the data to an IO device, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa get() post() request()
+*/
+QIODevice* Q3Http::currentDestinationDevice() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( !r )
+ return 0;
+ return r->destinationDevice();
+}
+
+/*!
+ Returns true if there are any requests scheduled that have not yet
+ been executed; otherwise returns false.
+
+ The request that is being executed is \e not considered as a
+ scheduled request.
+
+ \sa clearPendingRequests() currentId() currentRequest()
+*/
+bool Q3Http::hasPendingRequests() const
+{
+ return d->pending.count() > 1;
+}
+
+/*!
+ Deletes all pending requests from the list of scheduled requests.
+ This does not affect the request that is being executed. If
+ you want to stop this as well, use abort().
+
+ \sa hasPendingRequests() abort()
+*/
+void Q3Http::clearPendingRequests()
+{
+ Q3HttpRequest *r = 0;
+ if ( d->pending.count() > 0 )
+ r = d->pending.take( 0 );
+ d->pending.clear();
+ if ( r )
+ d->pending.append( r );
+}
+
+/*!
+ Sets the HTTP server that is used for requests to \a hostname on
+ port \a port.
+
+ The function does not block and returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa get() post() head() request() requestStarted() requestFinished() done()
+*/
+int Q3Http::setHost(const QString &hostname, Q_UINT16 port )
+{
+ return addRequest( new Q3HttpSetHostRequest( hostname, port ) );
+}
+
+/*!
+ Sends a get request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html.
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ The function does not block and returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() post() head() request() requestStarted() requestFinished() done()
+*/
+int Q3Http::get( const QString& path, QIODevice* to )
+{
+ Q3HttpRequestHeader header( QLatin1String("GET"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( header, (QIODevice*)0, to ) );
+}
+
+/*!
+ Sends a post request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html.
+
+ The incoming data comes via the \a data IO device.
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ The function does not block and returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() head() request() requestStarted() requestFinished() done()
+*/
+int Q3Http::post( const QString& path, QIODevice* data, QIODevice* to )
+{
+ Q3HttpRequestHeader header( QLatin1String("POST"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( header, data, to ) );
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int Q3Http::post( const QString& path, const QByteArray& data, QIODevice* to )
+{
+ Q3HttpRequestHeader header( QLatin1String("POST"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( header, new QByteArray(data), to ) );
+}
+
+/*!
+ Sends a header request for \a path to the server set by setHost()
+ or as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html.
+
+ The function does not block and returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() post() request() requestStarted() requestFinished() done()
+*/
+int Q3Http::head( const QString& path )
+{
+ Q3HttpRequestHeader header( QLatin1String("HEAD"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( header, (QIODevice*)0, 0 ) );
+}
+
+/*!
+ Sends a request to the server set by setHost() or as specified in
+ the constructor. Uses the \a header as the HTTP request header.
+ You are responsible for setting up a header that is appropriate
+ for your request.
+
+ The incoming data comes via the \a data IO device.
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ The function does not block and returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() post() head() requestStarted() requestFinished() done()
+*/
+int Q3Http::request( const Q3HttpRequestHeader &header, QIODevice *data, QIODevice *to )
+{
+ return addRequest( new Q3HttpNormalRequest( header, data, to ) );
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int Q3Http::request( const Q3HttpRequestHeader &header, const QByteArray &data, QIODevice *to )
+{
+ return addRequest( new Q3HttpNormalRequest( header, new QByteArray(data), to ) );
+}
+
+/*!
+ Closes the connection; this is useful if you have a keep-alive
+ connection and want to close it.
+
+ For the requests issued with get(), post() and head(), Q3Http sets
+ the connection to be keep-alive. You can also do this using the
+ header you pass to the request() function. Q3Http only closes the
+ connection to the HTTP server if the response header requires it
+ to do so.
+
+ The function does not block and returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ If you want to close the connection immediately, you have to use
+ abort() instead.
+
+ \sa stateChanged() abort() requestStarted() requestFinished() done()
+*/
+int Q3Http::closeConnection()
+{
+ return addRequest( new Q3HttpCloseRequest() );
+}
+
+int Q3Http::addRequest( Q3HttpRequest *req )
+{
+ d->pending.append( req );
+
+ if ( d->pending.count() == 1 )
+ // don't emit the requestStarted() signal before the id is returned
+ QTimer::singleShot( 0, this, SLOT(startNextRequest()) );
+
+ return req->id;
+}
+
+void Q3Http::startNextRequest()
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ d->error = NoError;
+ d->errorString = QHttp::tr( "Unknown error" );
+
+ if ( bytesAvailable() )
+ readAll(); // clear the data
+ emit requestStarted( r->id );
+ r->start( this );
+}
+
+void Q3Http::sendRequest()
+{
+ if ( d->hostname.isNull() ) {
+ finishedWithError( QHttp::tr("No server set to connect to"), UnknownError );
+ return;
+ }
+
+ killIdleTimer();
+
+ // Do we need to setup a new connection or can we reuse an
+ // existing one ?
+ if ( d->socket.peerName() != d->hostname || d->socket.peerPort() != d->port
+ || d->socket.state() != Q3Socket::Connection ) {
+ setState( Q3Http::Connecting );
+ d->socket.connectToHost( d->hostname, d->port );
+ } else {
+ slotConnected();
+ }
+
+}
+
+void Q3Http::finishedWithSuccess()
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ emit requestFinished( r->id, false );
+ d->pending.removeFirst();
+ if ( d->pending.isEmpty() ) {
+ emit done( false );
+ } else {
+ startNextRequest();
+ }
+}
+
+void Q3Http::finishedWithError( const QString& detail, int errorCode )
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ d->error = (Error)errorCode;
+ d->errorString = detail;
+ emit requestFinished( r->id, true );
+
+ d->pending.clear();
+ emit done( true );
+}
+
+void Q3Http::slotClosed()
+{
+ if ( d->state == Closing )
+ return;
+
+ if ( d->state == Reading ) {
+ if ( d->response.hasKey( QLatin1String("content-length") ) ) {
+ // We got Content-Length, so did we get all bytes?
+ if ( d->bytesDone+bytesAvailable() != d->response.contentLength() ) {
+ finishedWithError( QHttp::tr("Wrong content length"), WrongContentLength );
+ }
+ }
+ } else if ( d->state == Connecting || d->state == Sending ) {
+ finishedWithError( QHttp::tr("Server closed connection unexpectedly"), UnexpectedClose );
+ }
+
+ d->postDevice = 0;
+ setState( Closing );
+ d->idleTimer = startTimer( 0 );
+}
+
+void Q3Http::slotConnected()
+{
+ if ( d->state != Sending ) {
+ d->bytesDone = 0;
+ setState( Sending );
+ }
+
+ QString str = d->header.toString();
+ d->bytesTotal = str.length();
+ d->socket.writeBlock( str.latin1(), d->bytesTotal );
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http: write request header:\n---{\n%s}---", str.latin1() );
+#endif
+
+ if ( d->postDevice ) {
+ d->bytesTotal += d->postDevice->size();
+ } else {
+ d->bytesTotal += d->buffer.size();
+ d->socket.writeBlock( d->buffer.data(), d->buffer.size() );
+ d->buffer = QByteArray(); // save memory
+ }
+}
+
+void Q3Http::slotError( int err )
+{
+ d->postDevice = 0;
+
+ if ( d->state == Connecting || d->state == Reading || d->state == Sending ) {
+ switch ( err ) {
+ case Q3Socket::ErrConnectionRefused:
+ finishedWithError( QHttp::tr("Connection refused"), ConnectionRefused );
+ break;
+ case Q3Socket::ErrHostNotFound:
+ finishedWithError( QHttp::tr("Host %1 not found").arg(d->socket.peerName()), HostNotFound );
+ break;
+ default:
+ finishedWithError( QHttp::tr("HTTP request failed"), UnknownError );
+ break;
+ }
+ }
+
+ close();
+}
+
+void Q3Http::slotBytesWritten( int written )
+{
+ d->bytesDone += written;
+ emit dataSendProgress( d->bytesDone, d->bytesTotal );
+
+ if ( !d->postDevice )
+ return;
+
+ if ( d->socket.bytesToWrite() == 0 ) {
+ int max = qMin<int>( 4096, d->postDevice->size() - d->postDevice->at() );
+ QByteArray arr( max );
+
+ int n = d->postDevice->readBlock( arr.data(), max );
+ if ( n != max ) {
+ qWarning("Could not read enough bytes from the device");
+ close();
+ return;
+ }
+ if ( d->postDevice->atEnd() ) {
+ d->postDevice = 0;
+ }
+
+ d->socket.writeBlock( arr.data(), max );
+ }
+}
+
+void Q3Http::slotReadyRead()
+{
+ if ( d->state != Reading ) {
+ setState( Reading );
+ d->buffer = QByteArray();
+ d->readHeader = true;
+ d->headerStr = QLatin1String("");
+ d->bytesDone = 0;
+ d->chunkedSize = -1;
+ }
+
+ while ( d->readHeader ) {
+ bool end = false;
+ QString tmp;
+ while ( !end && d->socket.canReadLine() ) {
+ tmp = QLatin1String(d->socket.readLine());
+ if ( tmp == QLatin1String("\r\n") || tmp == QLatin1String("\n") )
+ end = true;
+ else
+ d->headerStr += tmp;
+ }
+
+ if ( !end )
+ return;
+
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http: read response header:\n---{\n%s}---", d->headerStr.latin1() );
+#endif
+ d->response = Q3HttpResponseHeader( d->headerStr );
+ d->headerStr = QLatin1String("");
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http: read response header:\n---{\n%s}---", d->response.toString().latin1() );
+#endif
+ // Check header
+ if ( !d->response.isValid() ) {
+ finishedWithError( QHttp::tr("Invalid HTTP response header"), InvalidResponseHeader );
+ close();
+ return;
+ }
+
+ // The 100-continue header is ignored, because when using the
+ // POST method, we send both the request header and data in
+ // one chunk.
+ if (d->response.statusCode() != 100) {
+ d->readHeader = false;
+ if ( d->response.hasKey( QLatin1String("transfer-encoding") ) &&
+ d->response.value( QLatin1String("transfer-encoding") ).lower().contains( QLatin1String("chunked") ) )
+ d->chunkedSize = 0;
+
+ emit responseHeaderReceived( d->response );
+ }
+ }
+
+ if ( !d->readHeader ) {
+ bool everythingRead = false;
+
+ if ( currentRequest().method() == QLatin1String("HEAD") ) {
+ everythingRead = true;
+ } else {
+ Q_ULONG n = d->socket.bytesAvailable();
+ QByteArray *arr = 0;
+ if ( d->chunkedSize != -1 ) {
+ // transfer-encoding is chunked
+ for ( ;; ) {
+ // get chunk size
+ if ( d->chunkedSize == 0 ) {
+ if ( !d->socket.canReadLine() )
+ break;
+ QString sizeString = QLatin1String(d->socket.readLine());
+ int tPos = sizeString.find( QLatin1Char(';') );
+ if ( tPos != -1 )
+ sizeString.truncate( tPos );
+ bool ok;
+ d->chunkedSize = sizeString.toInt( &ok, 16 );
+ if ( !ok ) {
+ finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength );
+ close();
+ delete arr;
+ return;
+ }
+ if ( d->chunkedSize == 0 ) // last-chunk
+ d->chunkedSize = -2;
+ }
+
+ // read trailer
+ while ( d->chunkedSize == -2 && d->socket.canReadLine() ) {
+ QString read = QLatin1String(d->socket.readLine());
+ if ( read == QLatin1String("\r\n") || read == QLatin1String("\n") )
+ d->chunkedSize = -1;
+ }
+ if ( d->chunkedSize == -1 ) {
+ everythingRead = true;
+ break;
+ }
+
+ // make sure that you can read the terminating CRLF,
+ // otherwise wait until next time...
+ n = d->socket.bytesAvailable();
+ if ( n == 0 )
+ break;
+ if ( (Q_LONG)n == d->chunkedSize || (Q_LONG)n == d->chunkedSize+1 ) {
+ n = d->chunkedSize - 1;
+ if ( n == 0 )
+ break;
+ }
+
+ // read data
+ uint toRead = QMIN( (Q_LONG)n, (d->chunkedSize < 0 ? (Q_LONG)n : d->chunkedSize) );
+ if ( !arr )
+ arr = new QByteArray( 0 );
+ uint oldArrSize = arr->size();
+ arr->resize( oldArrSize + toRead );
+ Q_LONG read = d->socket.readBlock( arr->data()+oldArrSize, toRead );
+ arr->resize( oldArrSize + read );
+
+ d->chunkedSize -= read;
+
+ if ( d->chunkedSize == 0 && n - read >= 2 ) {
+ // read terminating CRLF
+ char tmp[2];
+ d->socket.readBlock( tmp, 2 );
+ if ( tmp[0] != '\r' || tmp[1] != '\n' ) {
+ finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength );
+ close();
+ delete arr;
+ return;
+ }
+ }
+ }
+ } else if ( d->response.hasContentLength() ) {
+ n = qMin<ulong>( d->response.contentLength() - d->bytesDone, n );
+ if ( n > 0 ) {
+ arr = new QByteArray( n );
+ Q_LONG read = d->socket.readBlock( arr->data(), n );
+ arr->resize( read );
+ }
+ if ( d->bytesDone + bytesAvailable() + n == d->response.contentLength() )
+ everythingRead = true;
+ } else if ( n > 0 ) {
+ // workaround for VC++ bug
+ QByteArray temp = d->socket.readAll();
+ arr = new QByteArray( temp );
+ }
+
+ if ( arr ) {
+ n = arr->size();
+ if ( d->toDevice ) {
+ d->toDevice->writeBlock( arr->data(), n );
+ delete arr;
+ d->bytesDone += n;
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%d bytes done)", n, d->bytesDone );
+#endif
+ if ( d->response.hasContentLength() )
+ emit dataReadProgress( d->bytesDone, d->response.contentLength() );
+ else
+ emit dataReadProgress( d->bytesDone, 0 );
+ } else {
+ d->rba.append( arr );
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%ld bytes done)", n, d->bytesDone + bytesAvailable() );
+#endif
+ if ( d->response.hasContentLength() )
+ emit dataReadProgress( d->bytesDone + bytesAvailable(), d->response.contentLength() );
+ else
+ emit dataReadProgress( d->bytesDone + bytesAvailable(), 0 );
+ emit readyRead( d->response );
+ }
+ }
+ }
+
+ if ( everythingRead ) {
+ // Handle "Connection: close"
+ if ( d->response.value(QLatin1String("connection")).lower() == QLatin1String("close") ) {
+ close();
+ } else {
+ setState( Connected );
+ // Start a timer, so that we emit the keep alive signal
+ // "after" this method returned.
+ d->idleTimer = startTimer( 0 );
+ }
+ }
+ }
+}
+
+/*!
+ Returns the current state of the object. When the state changes,
+ the stateChanged() signal is emitted.
+
+ \sa State stateChanged()
+*/
+Q3Http::State Q3Http::state() const
+{
+ return d->state;
+}
+
+/*!
+ Returns the last error that occurred. This is useful to find out
+ what happened when receiving a requestFinished() or a done()
+ signal with the \c error argument \c true.
+
+ If you start a new request, the error status is reset to \c NoError.
+*/
+Q3Http::Error Q3Http::error() const
+{
+ return d->error;
+}
+
+/*!
+ Returns a human-readable description of the last error that
+ occurred. This is useful to present a error message to the user
+ when receiving a requestFinished() or a done() signal with the \c
+ error argument \c true.
+*/
+QString Q3Http::errorString() const
+{
+ return d->errorString;
+}
+
+/*! \reimp
+*/
+void Q3Http::timerEvent( QTimerEvent *e )
+{
+ if ( e->timerId() == d->idleTimer ) {
+ killTimer( d->idleTimer );
+ d->idleTimer = 0;
+
+ if ( d->state == Connected ) {
+ finishedWithSuccess();
+ } else if ( d->state != Unconnected ) {
+ setState( Unconnected );
+ finishedWithSuccess();
+ }
+ } else {
+ QObject::timerEvent( e );
+ }
+}
+
+void Q3Http::killIdleTimer()
+{
+ if (d->idleTimer)
+ killTimer( d->idleTimer );
+ d->idleTimer = 0;
+}
+
+void Q3Http::setState( int s )
+{
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http state changed %d -> %d", d->state, s );
+#endif
+ d->state = (State)s;
+ emit stateChanged( s );
+}
+
+void Q3Http::close()
+{
+ // If no connection is open -> ignore
+ if ( d->state == Closing || d->state == Unconnected )
+ return;
+
+ d->postDevice = 0;
+ setState( Closing );
+
+ // Already closed ?
+ if ( !d->socket.isOpen() ) {
+ d->idleTimer = startTimer( 0 );
+ } else {
+ // Close now.
+ d->socket.close();
+
+ // Did close succeed immediately ?
+ if ( d->socket.state() == Q3Socket::Idle ) {
+ // Prepare to emit the requestFinished() signal.
+ d->idleTimer = startTimer( 0 );
+ }
+ }
+}
+
+/**********************************************************************
+ *
+ * Q3Http implementation of the Q3NetworkProtocol interface
+ *
+ *********************************************************************/
+/*! \reimp
+*/
+int Q3Http::supportedOperations() const
+{
+ return OpGet | OpPut;
+}
+
+/*! \reimp
+*/
+void Q3Http::operationGet( Q3NetworkOperation *op )
+{
+ connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
+ this, SLOT(clientReply(Q3HttpResponseHeader)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ bytesRead = 0;
+ op->setState( StInProgress );
+ Q3Url u( operationInProgress()->arg( 0 ) );
+ Q3HttpRequestHeader header( QLatin1String("GET"), u.encodedPathAndQuery(), 1, 0 );
+ header.setValue( QLatin1String("Host"), u.host() );
+ setHost( u.host(), u.port() != -1 ? u.port() : 80 );
+ request( header );
+}
+
+/*! \reimp
+*/
+void Q3Http::operationPut( Q3NetworkOperation *op )
+{
+ connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
+ this, SLOT(clientReply(Q3HttpResponseHeader)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ bytesRead = 0;
+ op->setState( StInProgress );
+ Q3Url u( operationInProgress()->arg( 0 ) );
+ Q3HttpRequestHeader header( QLatin1String("POST"), u.encodedPathAndQuery(), 1, 0 );
+ header.setValue( QLatin1String("Host"), u.host() );
+ setHost( u.host(), u.port() != -1 ? u.port() : 80 );
+ request( header, op->rawArg(1) );
+}
+
+void Q3Http::clientReply( const Q3HttpResponseHeader &rep )
+{
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( rep.statusCode() >= 400 && rep.statusCode() < 600 ) {
+ op->setState( StFailed );
+ op->setProtocolDetail(
+ QString::fromLatin1("%1 %2").arg(rep.statusCode()).arg(rep.reasonPhrase())
+ );
+ switch ( rep.statusCode() ) {
+ case 401:
+ case 403:
+ case 405:
+ op->setErrorCode( ErrPermissionDenied );
+ break;
+ case 404:
+ op->setErrorCode(ErrFileNotExisting );
+ break;
+ default:
+ if ( op->operation() == OpGet )
+ op->setErrorCode( ErrGet );
+ else
+ op->setErrorCode( ErrPut );
+ break;
+ }
+ }
+ // ### In cases of an error, should we still emit the data() signals?
+ if ( op->operation() == OpGet && bytesAvailable() > 0 ) {
+ QByteArray ba = readAll();
+ emit data( ba, op );
+ bytesRead += ba.size();
+ if ( rep.hasContentLength() ) {
+ emit dataTransferProgress( bytesRead, rep.contentLength(), op );
+ }
+ }
+ }
+}
+
+void Q3Http::clientDone( bool err )
+{
+ disconnect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
+ this, SLOT(clientReply(Q3HttpResponseHeader)) );
+ disconnect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ disconnect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ if ( err ) {
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ op->setState( Q3NetworkProtocol::StFailed );
+ op->setProtocolDetail( errorString() );
+ switch ( error() ) {
+ case ConnectionRefused:
+ op->setErrorCode( ErrHostNotFound );
+ break;
+ case HostNotFound:
+ op->setErrorCode( ErrHostNotFound );
+ break;
+ default:
+ if ( op->operation() == OpGet )
+ op->setErrorCode( ErrGet );
+ else
+ op->setErrorCode( ErrPut );
+ break;
+ }
+ emit finished( op );
+ }
+ } else {
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( op->state() != StFailed ) {
+ op->setState( Q3NetworkProtocol::StDone );
+ op->setErrorCode( Q3NetworkProtocol::NoError );
+ }
+ emit finished( op );
+ }
+ }
+
+}
+
+void Q3Http::clientStateChanged( int state )
+{
+ if ( url() ) {
+ switch ( (State)state ) {
+ case Connecting:
+ emit connectionStateChanged( ConHostFound, QHttp::tr( "Host %1 found" ).arg( url()->host() ) );
+ break;
+ case Sending:
+ emit connectionStateChanged( ConConnected, QHttp::tr( "Connected to host %1" ).arg( url()->host() ) );
+ break;
+ case Unconnected:
+ emit connectionStateChanged( ConClosed, QHttp::tr( "Connection to %1 closed" ).arg( url()->host() ) );
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch ( (State)state ) {
+ case Connecting:
+ emit connectionStateChanged( ConHostFound, QHttp::tr( "Host found" ) );
+ break;
+ case Sending:
+ emit connectionStateChanged( ConConnected, QHttp::tr( "Connected to host" ) );
+ break;
+ case Unconnected:
+ emit connectionStateChanged( ConClosed, QHttp::tr( "Connection closed" ) );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/network/q3http.h b/src/qt3support/network/q3http.h
new file mode 100644
index 0000000..2b52e29
--- /dev/null
+++ b/src/qt3support/network/q3http.h
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3HTTP_H
+#define Q3HTTP_H
+
+#include <QtCore/qobject.h>
+#include <Qt3Support/q3networkprotocol.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+#ifndef QT_NO_HTTP
+
+class Q3Socket;
+class QTimerEvent;
+class QTextStream;
+class QIODevice;
+
+class Q3HttpPrivate;
+class Q3HttpRequest;
+
+class Q_COMPAT_EXPORT Q3HttpHeader
+{
+public:
+ Q3HttpHeader();
+ Q3HttpHeader( const Q3HttpHeader& header );
+ Q3HttpHeader( const QString& str );
+ virtual ~Q3HttpHeader();
+
+ Q3HttpHeader& operator=( const Q3HttpHeader& h );
+
+ QString value( const QString& key ) const;
+ void setValue( const QString& key, const QString& value );
+ void removeValue( const QString& key );
+
+ QStringList keys() const;
+ bool hasKey( const QString& key ) const;
+
+ bool hasContentLength() const;
+ uint contentLength() const;
+ void setContentLength( int len );
+
+ bool hasContentType() const;
+ QString contentType() const;
+ void setContentType( const QString& type );
+
+ virtual QString toString() const;
+ bool isValid() const;
+
+ virtual int majorVersion() const = 0;
+ virtual int minorVersion() const = 0;
+
+protected:
+ virtual bool parseLine( const QString& line, int number );
+ bool parse( const QString& str );
+ void setValid( bool );
+
+private:
+ QMap<QString,QString> values;
+ bool valid;
+};
+
+class Q_COMPAT_EXPORT Q3HttpResponseHeader : public Q3HttpHeader
+{
+private:
+ Q3HttpResponseHeader( int code, const QString& text = QString(), int majorVer = 1, int minorVer = 1 );
+ Q3HttpResponseHeader( const QString& str );
+
+ void setStatusLine( int code, const QString& text = QString(), int majorVer = 1, int minorVer = 1 );
+
+public:
+ Q3HttpResponseHeader();
+ Q3HttpResponseHeader( const Q3HttpResponseHeader& header );
+
+ int statusCode() const;
+ QString reasonPhrase() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ QString toString() const;
+
+protected:
+ bool parseLine( const QString& line, int number );
+
+private:
+ int statCode;
+ QString reasonPhr;
+ int majVer;
+ int minVer;
+
+ friend class Q3Http;
+};
+
+class Q_COMPAT_EXPORT Q3HttpRequestHeader : public Q3HttpHeader
+{
+public:
+ Q3HttpRequestHeader();
+ Q3HttpRequestHeader( const QString& method, const QString& path, int majorVer = 1, int minorVer = 1 );
+ Q3HttpRequestHeader( const Q3HttpRequestHeader& header );
+ Q3HttpRequestHeader( const QString& str );
+
+ void setRequest( const QString& method, const QString& path, int majorVer = 1, int minorVer = 1 );
+
+ QString method() const;
+ QString path() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ QString toString() const;
+
+protected:
+ bool parseLine( const QString& line, int number );
+
+private:
+ QString m;
+ QString p;
+ int majVer;
+ int minVer;
+};
+
+class Q_COMPAT_EXPORT Q3Http : public Q3NetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ Q3Http();
+ Q3Http( QObject* parent, const char* name = 0 ); // ### Qt 4.0: make parent=0 and get rid of the Q3Http() constructor
+ Q3Http( const QString &hostname, Q_UINT16 port=80, QObject* parent=0, const char* name = 0 );
+ virtual ~Q3Http();
+
+ int supportedOperations() const;
+
+ enum State { Unconnected, HostLookup, Connecting, Sending, Reading, Connected, Closing };
+ enum Error {
+ NoError,
+ UnknownError,
+ HostNotFound,
+ ConnectionRefused,
+ UnexpectedClose,
+ InvalidResponseHeader,
+ WrongContentLength,
+ Aborted
+ };
+
+ int setHost(const QString &hostname, Q_UINT16 port=80 );
+
+ int get( const QString& path, QIODevice* to=0 );
+ int post( const QString& path, QIODevice* data, QIODevice* to=0 );
+ int post( const QString& path, const QByteArray& data, QIODevice* to=0 );
+ int head( const QString& path );
+ int request( const Q3HttpRequestHeader &header, QIODevice *device=0, QIODevice *to=0 );
+ int request( const Q3HttpRequestHeader &header, const QByteArray &data, QIODevice *to=0 );
+
+ int closeConnection();
+
+ Q_ULONG bytesAvailable() const;
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ QByteArray readAll();
+
+ int currentId() const;
+ QIODevice* currentSourceDevice() const;
+ QIODevice* currentDestinationDevice() const;
+ Q3HttpRequestHeader currentRequest() const;
+ bool hasPendingRequests() const;
+ void clearPendingRequests();
+
+ State state() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void abort();
+
+Q_SIGNALS:
+ void stateChanged( int );
+ void responseHeaderReceived( const Q3HttpResponseHeader& resp );
+ void readyRead( const Q3HttpResponseHeader& resp );
+ void dataSendProgress( int, int );
+ void dataReadProgress( int, int );
+
+ void requestStarted( int );
+ void requestFinished( int, bool );
+ void done( bool );
+
+protected:
+ void operationGet( Q3NetworkOperation *op );
+ void operationPut( Q3NetworkOperation *op );
+
+ void timerEvent( QTimerEvent * );
+
+private Q_SLOTS:
+ void clientReply( const Q3HttpResponseHeader &rep );
+ void clientDone( bool );
+ void clientStateChanged( int );
+
+ void startNextRequest();
+ void slotReadyRead();
+ void slotConnected();
+ void slotError( int );
+ void slotClosed();
+ void slotBytesWritten( int );
+
+private:
+ Q3HttpPrivate *d;
+ void *unused; // ### Qt 4.0: remove this (in for binary compatibility)
+ int bytesRead;
+
+ int addRequest( Q3HttpRequest * );
+ void sendRequest();
+ void finishedWithSuccess();
+ void finishedWithError( const QString& detail, int errorCode );
+
+ void killIdleTimer();
+
+ void init();
+ void setState( int );
+ void close();
+
+ friend class Q3HttpNormalRequest;
+ friend class Q3HttpSetHostRequest;
+ friend class Q3HttpCloseRequest;
+ friend class Q3HttpPGHRequest;
+};
+
+#endif // QT_NO_HTTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3HTTP_H
diff --git a/src/qt3support/network/q3localfs.cpp b/src/qt3support/network/q3localfs.cpp
new file mode 100644
index 0000000..8dd6cd2
--- /dev/null
+++ b/src/qt3support/network/q3localfs.cpp
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3localfs.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+#include "qfileinfo.h"
+#include "qfile.h"
+#include "q3url.h"
+#include "qurlinfo.h"
+#include "qapplication.h"
+#include "q3urloperator.h"
+#include "qpointer.h"
+#include "q3valuelist.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define QLOCALFS_DEBUG
+
+
+/*!
+ \class Q3LocalFs
+ \brief The Q3LocalFs class is an implementation of a
+ QNetworkProtocol that works on the local file system.
+
+ \compat
+
+ This class is derived from QNetworkProtocol. Q3LocalFs is not
+ normally used directly, but rather through a QUrlOperator, for
+ example:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3localfs.cpp 0
+
+ This code will only work if the Q3LocalFs class is registered; to
+ register the class, you must call qInitNetworkProtocols() before
+ using a QUrlOperator with Q3LocalFs.
+
+ If you really need to use Q3LocalFs directly, don't forget
+ to set its QUrlOperator with setUrl().
+
+ \sa Q3NetworkProtocol, Q3UrlOperator
+*/
+
+/*!
+ Constructor.
+*/
+
+Q3LocalFs::Q3LocalFs()
+ : Q3NetworkProtocol()
+{
+}
+
+static int convertPermissions(QFileInfo *fi)
+{
+ int p = 0;
+ if ( fi->permission( QFileInfo::ReadOwner ) )
+ p |= QUrlInfo::ReadOwner;
+ if ( fi->permission( QFileInfo::WriteOwner ) )
+ p |= QUrlInfo::WriteOwner;
+ if ( fi->permission( QFileInfo::ExeOwner ) )
+ p |= QUrlInfo::ExeOwner;
+ if ( fi->permission( QFileInfo::ReadGroup ) )
+ p |= QUrlInfo::ReadGroup;
+ if ( fi->permission( QFileInfo::WriteGroup ) )
+ p |= QUrlInfo::WriteGroup;
+ if ( fi->permission( QFileInfo::ExeGroup ) )
+ p |= QUrlInfo::ExeGroup;
+ if ( fi->permission( QFileInfo::ReadOther ) )
+ p |= QUrlInfo::ReadOther;
+ if ( fi->permission( QFileInfo::WriteOther ) )
+ p |= QUrlInfo::WriteOther;
+ if ( fi->permission( QFileInfo::ExeOther ) )
+ p |= QUrlInfo::ExeOther;
+ return p;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationListChildren( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationListChildren" );
+#endif
+ op->setState( StInProgress );
+
+ dir = QDir( url()->path() );
+ dir.setNameFilter( url()->nameFilter() );
+ dir.setMatchAllDirs( true );
+ if ( !dir.isReadable() ) {
+ QString msg = tr( "Could not read directory\n%1" ).arg( url()->path() );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrListChildren );
+ emit finished( op );
+ return;
+ }
+
+ QFileInfoList filist = dir.entryInfoList(QDir::All | QDir::Hidden | QDir::System);
+ if ( filist.isEmpty() ) {
+ QString msg = tr( "Could not read directory\n%1" ).arg( url()->path() );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrListChildren );
+ emit finished( op );
+ return;
+ }
+
+ emit start( op );
+
+ Q3ValueList<QUrlInfo> infos;
+ for (int i = 0; i < filist.size(); ++i) {
+ QFileInfo fi = filist.at(i);
+ infos << QUrlInfo( fi.fileName(), convertPermissions(&fi), fi.owner(), fi.group(),
+ fi.size(), fi.lastModified(), fi.lastRead(), fi.isDir(), fi.isFile(),
+ fi.isSymLink(), fi.isWritable(), fi.isReadable(), fi.isExecutable() );
+ }
+ emit newChildren( infos, op );
+ op->setState( StDone );
+ emit finished( op );
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationMkDir( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationMkDir" );
+#endif
+ op->setState( StInProgress );
+ QString dirname = op->arg( 0 );
+
+ dir = QDir( url()->path() );
+ if ( dir.mkdir( dirname ) ) {
+ QFileInfo fi( dir, dirname );
+ QUrlInfo inf( fi.fileName(), convertPermissions(&fi), fi.owner(), fi.group(),
+ fi.size(), fi.lastModified(), fi.lastRead(), fi.isDir(), fi.isFile(),
+ fi.isSymLink(), fi.isWritable(), fi.isReadable(), fi.isExecutable() );
+ emit newChild( inf, op );
+ op->setState( StDone );
+ emit createdDirectory( inf, op );
+ emit finished( op );
+ } else {
+ QString msg = tr( "Could not create directory\n%1" ).arg( dirname );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrMkDir );
+ emit finished( op );
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationRemove( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationRemove" );
+#endif
+ op->setState( StInProgress );
+ QString name = Q3Url( op->arg( 0 ) ).path();
+ bool deleted = false;
+
+ dir = QDir( url()->path() );
+
+ QFileInfo fi( dir, name );
+ if ( fi.isDir() ) {
+ if ( dir.rmdir( name ) )
+ deleted = true;
+ }
+
+ if ( deleted || dir.remove( name ) ) {
+ op->setState( StDone );
+ emit removed( op );
+ emit finished( op );
+ } else {
+ QString msg = tr( "Could not remove file or directory\n%1" ).arg( name );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrRemove );
+ emit finished( op );
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationRename( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationRename" );
+#endif
+ op->setState( StInProgress );
+ QString oldname = op->arg( 0 );
+ QString newname = op->arg( 1 );
+
+ dir = QDir( url()->path() );
+ if ( dir.rename( oldname, newname ) ) {
+ op->setState( StDone );
+ emit itemChanged( op );
+ emit finished( op );
+ } else {
+ QString msg = tr( "Could not rename\n%1\nto\n%2" ).arg( oldname ).arg( newname );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrRename );
+ emit finished( op );
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationGet( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationGet" );
+#endif
+ op->setState( StInProgress );
+ QString from = Q3Url( op->arg( 0 ) ).path();
+
+ QFile f( from );
+ if ( !f.open( IO_ReadOnly ) ) {
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: could not open %s", from.latin1() );
+#endif
+ QString msg = tr( "Could not open\n%1" ).arg( from );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrGet );
+ emit finished( op );
+ return;
+ }
+
+ QByteArray s;
+ emit dataTransferProgress( 0, f.size(), op );
+ if ( f.size() != 0 ) {
+ int blockSize = calcBlockSize( f.size() );
+ if ( (int)f.size() < blockSize ) {
+ s.resize( f.size() );
+ f.readBlock( s.data(), f.size() );
+ emit data( s, op );
+ emit dataTransferProgress( f.size(), f.size(), op );
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: got all %d bytes at once", f.size() );
+#endif
+ } else {
+ s.resize( blockSize );
+ int remaining = f.size();
+ QPointer<QObject> that = this;
+ while ( that && remaining > 0 ) {
+ if ( operationInProgress() != op )
+ return;
+ if ( remaining >= blockSize ) {
+ f.readBlock( s.data(), blockSize );
+ emit data( s, op );
+ emit dataTransferProgress( f.size() - remaining, f.size(), op );
+ remaining -= blockSize;
+ } else {
+ s.resize( remaining );
+ f.readBlock( s.data(), remaining );
+ emit data( s, op );
+ emit dataTransferProgress( f.size() - remaining, f.size(), op );
+ remaining -= remaining;
+ }
+ qApp->processEvents();
+ }
+ if ( !that )
+ return;
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: got all %d bytes step by step", f.size() );
+#endif
+ emit dataTransferProgress( f.size(), f.size(), op );
+ }
+ }
+ op->setState( StDone );
+ f.close();
+ emit finished( op );
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationPut( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationPut" );
+#endif
+ op->setState( StInProgress );
+ QString to = Q3Url( op->arg( 0 ) ).path();
+
+ QFile f( to );
+ if ( !f.open( IO_WriteOnly ) ) {
+ QString msg = tr( "Could not write\n%1" ).arg( to );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrPut );
+ emit finished( op );
+ return;
+ }
+
+ QByteArray ba( op->rawArg( 1 ) );
+ emit dataTransferProgress( 0, ba.size(), op );
+ int blockSize = calcBlockSize( ba.size() );
+ if ( (int)ba.size() < blockSize ) {
+ f.writeBlock( ba.data(), ba.size() );
+ emit dataTransferProgress( ba.size(), ba.size(), op );
+ } else {
+ int i = 0;
+ while ( i + blockSize < (int)ba.size() - 1 ) {
+ if ( operationInProgress() != op )
+ return;
+ f.writeBlock( &ba.data()[ i ], blockSize );
+ f.flush();
+ emit dataTransferProgress( i + blockSize, ba.size(), op );
+ i += blockSize;
+ QPointer<QObject> that = this;
+ qApp->processEvents();
+ if (!that)
+ return;
+ }
+ if ( i < (int)ba.size() - 1 )
+ f.writeBlock( &ba.data()[ i ], ba.size() - i );
+ emit dataTransferProgress( ba.size(), ba.size(), op );
+ }
+ op->setState( StDone );
+ f.close();
+ emit finished( op );
+}
+
+/*!
+ \reimp
+*/
+
+int Q3LocalFs::supportedOperations() const
+{
+ return OpListChildren | OpMkDir | OpRemove | OpRename | OpGet | OpPut;
+}
+
+/*!
+ \internal
+*/
+
+int Q3LocalFs::calcBlockSize( int totalSize ) const
+{
+ if ( totalSize == 0 )
+ return 1024;
+ int s = totalSize / 100;
+ // we want a block size between 1KB and 1MB
+ if ( s < 1024 )
+ s = 1024;
+ if ( s > 1048576 )
+ s = 1048576;
+ return s;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKPROTOCOL
diff --git a/src/qt3support/network/q3localfs.h b/src/qt3support/network/q3localfs.h
new file mode 100644
index 0000000..54ec7fc
--- /dev/null
+++ b/src/qt3support/network/q3localfs.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3LOCALFS_H
+#define Q3LOCALFS_H
+
+#include <Qt3Support/q3networkprotocol.h>
+#include <QtCore/qdir.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+class Q_COMPAT_EXPORT Q3LocalFs : public Q3NetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ Q3LocalFs();
+ virtual int supportedOperations() const;
+
+protected:
+ virtual void operationListChildren( Q3NetworkOperation *op );
+ virtual void operationMkDir( Q3NetworkOperation *op );
+ virtual void operationRemove( Q3NetworkOperation *op );
+ virtual void operationRename( Q3NetworkOperation *op );
+ virtual void operationGet( Q3NetworkOperation *op );
+ virtual void operationPut( Q3NetworkOperation *op );
+
+private:
+ int calcBlockSize( int totalSize ) const;
+ QDir dir;
+
+};
+
+#endif // QT_NO_NETWORKPROTOCOL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3LOCALFS_H
diff --git a/src/qt3support/network/q3network.cpp b/src/qt3support/network/q3network.cpp
new file mode 100644
index 0000000..f06bff7
--- /dev/null
+++ b/src/qt3support/network/q3network.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3network.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "q3networkprotocol.h"
+
+// protocols
+#include "q3ftp.h"
+#include "q3http.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \relates Q3UrlOperator
+
+ This function registers the network protocols for FTP and HTTP.
+ You must call this function before you use QUrlOperator for
+ these protocols.
+*/
+void q3InitNetworkProtocols()
+{
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("ftp"), new Q3NetworkProtocolFactory< Q3Ftp > );
+#endif
+#ifndef QT_NO_HTTP
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("http"), new Q3NetworkProtocolFactory< Q3Http > );
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORK
diff --git a/src/qt3support/network/q3network.h b/src/qt3support/network/q3network.h
new file mode 100644
index 0000000..370019a
--- /dev/null
+++ b/src/qt3support/network/q3network.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3NETWORK_H
+#define Q3NETWORK_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_NETWORK
+
+Q_COMPAT_EXPORT void q3InitNetworkProtocols();
+
+#endif // QT_NO_NETWORK
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3NETWORK_H
diff --git a/src/qt3support/network/q3networkprotocol.cpp b/src/qt3support/network/q3networkprotocol.cpp
new file mode 100644
index 0000000..1054959
--- /dev/null
+++ b/src/qt3support/network/q3networkprotocol.cpp
@@ -0,0 +1,1209 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3networkprotocol.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+#include "q3localfs.h"
+#include "q3urloperator.h"
+#include "qtimer.h"
+#include "qmap.h"
+#include "q3ptrqueue.h"
+#include "q3valuelist.h"
+#include "qurlinfo.h"
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3NETWORKPROTOCOL_DEBUG
+#define NETWORK_OP_DELAY 1000
+
+extern Q_COMPAT_EXPORT Q3NetworkProtocolDict *q3networkProtocolRegister;
+
+Q3NetworkProtocolDict *q3networkProtocolRegister = 0;
+
+class Q3NetworkProtocolPrivate
+{
+public:
+ Q3NetworkProtocolPrivate( Q3NetworkProtocol *p )
+ {
+ url = 0;
+ opInProgress = 0;
+ opStartTimer = new QTimer( p );
+ removeTimer = new QTimer( p );
+ operationQueue.setAutoDelete( false );
+ autoDelete = false;
+ removeInterval = 10000;
+ oldOps.setAutoDelete( false );
+ }
+
+ ~Q3NetworkProtocolPrivate()
+ {
+ removeTimer->stop();
+ if ( opInProgress ) {
+ if ( opInProgress == operationQueue.head() )
+ operationQueue.dequeue();
+ opInProgress->free();
+ }
+ while ( operationQueue.head() ) {
+ operationQueue.head()->free();
+ operationQueue.dequeue();
+ }
+ while ( oldOps.first() ) {
+ oldOps.first()->free();
+ oldOps.removeFirst();
+ }
+ delete opStartTimer;
+ }
+
+ Q3UrlOperator *url;
+ Q3PtrQueue< Q3NetworkOperation > operationQueue;
+ Q3NetworkOperation *opInProgress;
+ QTimer *opStartTimer, *removeTimer;
+ int removeInterval;
+ bool autoDelete;
+ Q3PtrList< Q3NetworkOperation > oldOps;
+};
+
+/*!
+ \class Q3NetworkProtocol
+ \brief The Q3NetworkProtocol class provides a common API for network protocols.
+
+ \compat
+
+ This is a base class which should be used for network protocols
+ implementations that can then be used in Qt (e.g. in the file
+ dialog) together with the Q3UrlOperator.
+
+ The easiest way to implement a new network protocol is to
+ reimplement the operation*() methods, e.g. operationGet(), etc.
+ Only the supported operations should be reimplemented. To specify
+ which operations are supported, also reimplement
+ supportedOperations() and return an int that is OR'd together
+ using the supported operations from the \l
+ Q3NetworkProtocol::Operation enum.
+
+ When you implement a network protocol this way, it is important to
+ emit the correct signals. Also, always emit the finished() signal
+ when an operation is done (on success \e and on failure). Qt
+ relies on correctly emitted finished() signals.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::newChildren( const Q3ValueList<QUrlInfo> &i, Q3NetworkOperation *op )
+
+ This signal is emitted after listChildren() was called and new
+ children (files) have been read from the list of files. \a i holds
+ the information about the new children. \a op is the pointer to
+ the operation object which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+
+ When implementing your own network protocol and reading children,
+ you usually don't read one child at once, but rather a list of
+ them. That's why this signal takes a list of QUrlInfo objects. If
+ you prefer to read just one child at a time you can use the
+ convenience signal newChild(), which takes a single QUrlInfo
+ object.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::newChild( const QUrlInfo &i, Q3NetworkOperation *op )
+
+ This signal is emitted if a new child (file) has been read.
+ Q3NetworkProtocol automatically connects it to a slot which creates
+ a list of QUrlInfo objects (with just one QUrlInfo \a i) and emits
+ the newChildren() signal with this list. \a op is the pointer to
+ the operation object which contains all the information about the
+ operation that has finished, including the state, etc.
+
+ This is just a convenience signal useful for implementing your own
+ network protocol. In all other cases connect to the newChildren()
+ signal with its list of QUrlInfo objects.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::finished( Q3NetworkOperation *op )
+
+ This signal is emitted when an operation finishes. This signal is
+ always emitted, for both success and failure. \a op is the pointer
+ to the operation object which contains all the information about
+ the operation, including the state, etc. Check the state and error
+ code of the operation object to determine whether or not the
+ operation was successful.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::start( Q3NetworkOperation *op )
+
+ Some operations (such as listChildren()) emit this signal when
+ they start processing the operation. \a op is the pointer to the
+ operation object which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::createdDirectory( const QUrlInfo &i, Q3NetworkOperation *op )
+
+ This signal is emitted when mkdir() has been successful and the
+ directory has been created. \a i holds the information about the
+ new directory. \a op is the pointer to the operation object which
+ contains all the information about the operation, including the
+ state, etc. Using op->arg( 0 ), you can get the file name of the
+ new directory.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::removed( Q3NetworkOperation *op )
+
+ This signal is emitted when remove() has been succesiisful and the
+ file has been removed. \a op holds the file name of the removed
+ file in the first argument, accessible with op->arg( 0 ). \a op is
+ the pointer to the operation object which contains all the
+ information about the operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::itemChanged( Q3NetworkOperation *op )
+
+ This signal is emitted whenever a file which is a child of this
+ URL has been changed, e.g. by successfully calling rename(). \a op
+ holds the original and the new file names in the first and second
+ arguments, accessible with op->arg( 0 ) and op->arg( 1 )
+ respectively. \a op is the pointer to the operation object which
+ contains all the information about the operation, including the
+ state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::data( const QByteArray &data,
+ Q3NetworkOperation *op )
+
+ This signal is emitted when new \a data has been received after
+ calling get() or put(). \a op holds the name of the file from
+ which data is retrieved or uploaded in its first argument, and the
+ (raw) data in its second argument. You can get them with
+ op->arg( 0 ) and op->rawArg( 1 ). \a op is the pointer to the
+ operation object, which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator (which is used by the network
+ protocol) emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *op )
+
+ This signal is emitted during the transfer of data (using put() or
+ get()). \a bytesDone is how many bytes of \a bytesTotal have been
+ transferred. \a bytesTotal may be -1, which means that the total
+ number of bytes is not known. \a op is the pointer to the
+ operation object which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::connectionStateChanged( int state, const QString &data )
+
+ This signal is emitted whenever the state of the connection of the
+ network protocol is changed. \a state describes the new state,
+ which is one of, \c ConHostFound, \c ConConnected or \c ConClosed.
+ \a data is a message text.
+*/
+
+/*!
+ \enum Q3NetworkProtocol::State
+
+ This enum contains the state that a Q3NetworkOperation can have.
+
+ \value StWaiting The operation is in the Q3NetworkProtocol's queue
+ waiting to be prcessed.
+
+ \value StInProgress The operation is being processed.
+
+ \value StDone The operation has been processed successfully.
+
+ \value StFailed The operation has been processed but an error occurred.
+
+ \value StStopped The operation has been processed but has been
+ stopped before it finished, and is waiting to be processed.
+
+*/
+
+/*!
+ \enum Q3NetworkProtocol::Operation
+
+ This enum lists the possible operations that a network protocol
+ can support. supportedOperations() returns an int of these that is
+ OR'd together. Also, the type() of a Q3NetworkOperation is always
+ one of these values.
+
+ \value OpListChildren List the children of a URL, e.g. of a directory.
+ \value OpMkDir Create a directory.
+ \value OpRemove Remove a child (e.g. a file).
+ \value OpRename Rename a child (e.g. a file).
+ \value OpGet Get data from a location.
+ \value OpPut Put data to a location.
+ \omitvalue OpMkdir
+*/
+
+/*!
+ \enum Q3NetworkProtocol::ConnectionState
+
+ When the connection state of a network protocol changes it emits
+ the signal connectionStateChanged(). The first argument is one of
+ the following values:
+
+ \value ConHostFound Host has been found.
+ \value ConConnected Connection to the host has been established.
+ \value ConClosed Connection has been closed.
+*/
+
+/*!
+ \enum Q3NetworkProtocol::Error
+
+ When an operation fails (finishes unsuccessfully), the
+ Q3NetworkOperation of the operation returns an error code which has
+ one of the following values:
+
+ \value NoError No error occurred.
+
+ \value ErrValid The URL you are operating on is not valid.
+
+ \value ErrUnknownProtocol There is no protocol implementation
+ available for the protocol of the URL you are operating on (e.g.
+ if the protocol is http and no http implementation has been
+ registered).
+
+ \value ErrUnsupported The operation is not supported by the
+ protocol.
+
+ \value ErrParse The URL could not be parsed correctly.
+
+ \value ErrLoginIncorrect You needed to login but the username
+ or password is wrong.
+
+ \value ErrHostNotFound The specified host (in the URL) couldn't
+ be found.
+
+ \value ErrListChildren An error occurred while listing the
+ children (files).
+
+ \value ErrMkDir An error occurred when creating a directory.
+
+ \value ErrRemove An error occurred when removing a child (file).
+
+ \value ErrRename An error occurred when renaming a child (file).
+
+ \value ErrGet An error occurred while getting (retrieving) data.
+
+ \value ErrPut An error occurred while putting (uploading) data.
+
+ \value ErrFileNotExisting A file which is needed by the operation
+ doesn't exist.
+
+ \value ErrPermissionDenied Permission for doing the operation has
+ been denied.
+ \omitvalue ErrMkdir
+ \omitvalue ErrListChlidren
+
+ You should also use these error codes when implementing custom
+ network protocols. If this is not possible, you can define your own
+ error codes by using integer values that don't conflict with any
+ of these values.
+*/
+
+/*!
+ Constructor of the network protocol base class. Does some
+ initialization and connecting of signals and slots.
+*/
+
+Q3NetworkProtocol::Q3NetworkProtocol()
+ : QObject()
+{
+ d = new Q3NetworkProtocolPrivate( this );
+
+ connect( d->opStartTimer, SIGNAL(timeout()),
+ this, SLOT(startOps()) );
+ connect( d->removeTimer, SIGNAL(timeout()),
+ this, SLOT(removeMe()) );
+
+ if ( url() ) {
+ connect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ url(), SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(start(Q3NetworkOperation*)),
+ url(), SIGNAL(start(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+ connect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+ url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(removed(Q3NetworkOperation*)),
+ url(), SIGNAL(removed(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(connectionStateChanged(int,QString)),
+ url(), SIGNAL(connectionStateChanged(int,QString)) );
+ }
+
+ connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(processNextOperation(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChild(QUrlInfo,Q3NetworkOperation*)),
+ this, SLOT(emitNewChildren(QUrlInfo,Q3NetworkOperation*)) );
+
+}
+
+/*!
+ Destructor.
+*/
+
+Q3NetworkProtocol::~Q3NetworkProtocol()
+{
+ delete d;
+}
+
+/*!
+ Sets the Q3UrlOperator, on which the protocol works, to \a u.
+
+ \sa Q3UrlOperator
+*/
+
+void Q3NetworkProtocol::setUrl( Q3UrlOperator *u )
+{
+ if ( url() ) {
+ disconnect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ url(), SIGNAL(finished(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(start(Q3NetworkOperation*)),
+ url(), SIGNAL(start(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+ disconnect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+ url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(removed(Q3NetworkOperation*)),
+ url(), SIGNAL(removed(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(connectionStateChanged(int,QString)),
+ url(), SIGNAL(connectionStateChanged(int,QString)) );
+ }
+
+
+ // ### if autoDelete is true, we should delete the Q3UrlOperator (something
+ // like below; but that is not possible since it would delete this, too).
+ //if ( d->autoDelete && (d->url!=u) ) {
+ // delete d->url; // destructor deletes the network protocol
+ //}
+ d->url = u;
+
+ if ( url() ) {
+ connect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ url(), SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(start(Q3NetworkOperation*)),
+ url(), SIGNAL(start(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+ connect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+ url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(removed(Q3NetworkOperation*)),
+ url(), SIGNAL(removed(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(connectionStateChanged(int,QString)),
+ url(), SIGNAL(connectionStateChanged(int,QString)) );
+ }
+
+ if ( !d->opInProgress && !d->operationQueue.isEmpty() )
+ d->opStartTimer->start( 0, true );
+}
+
+/*!
+ For processing operations the network protocol base class calls
+ this method quite often. This should be reimplemented by new
+ network protocols. It should return true if the connection is OK
+ (open); otherwise it should return false. If the connection is not
+ open the protocol should open it.
+
+ If the connection can't be opened (e.g. because you already tried
+ but the host couldn't be found), set the state of \a op to
+ Q3NetworkProtocol::StFailed and emit the finished() signal with
+ this Q3NetworkOperation as argument.
+
+ \a op is the operation that needs an open connection.
+*/
+
+bool Q3NetworkProtocol::checkConnection( Q3NetworkOperation * )
+{
+ return true;
+}
+
+/*!
+ Returns an int that is OR'd together using the enum values of
+ \l{Q3NetworkProtocol::Operation}, which describes which operations
+ are supported by the network protocol. Should be reimplemented by
+ new network protocols.
+*/
+
+int Q3NetworkProtocol::supportedOperations() const
+{
+ return 0;
+}
+
+/*!
+ Adds the operation \a op to the operation queue. The operation
+ will be processed as soon as possible. This method returns
+ immediately.
+*/
+
+void Q3NetworkProtocol::addOperation( Q3NetworkOperation *op )
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: addOperation: %p %d", op, op->operation() );
+#endif
+ d->operationQueue.enqueue( op );
+ if ( !d->opInProgress )
+ d->opStartTimer->start( 0, true );
+}
+
+/*!
+ Static method to register a network protocol for Qt. For example,
+ if you have an implementation of NNTP (called Nntp) which is
+ derived from Q3NetworkProtocol, call:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3networkprotocol.cpp 0
+ after which your implementation is registered for future nntp
+ operations.
+
+ The name of the protocol is given in \a protocol and a pointer to
+ the protocol factory is given in \a protocolFactory.
+*/
+
+void Q3NetworkProtocol::registerNetworkProtocol( const QString &protocol,
+ Q3NetworkProtocolFactoryBase *protocolFactory )
+{
+ if ( !q3networkProtocolRegister ) {
+ q3networkProtocolRegister = new Q3NetworkProtocolDict;
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("file"), new Q3NetworkProtocolFactory< Q3LocalFs > );
+ }
+
+ q3networkProtocolRegister->insert( protocol, protocolFactory );
+}
+
+/*!
+ Static method to get a new instance of the network protocol \a
+ protocol. For example, if you need to do some FTP operations, do
+ the following:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3networkprotocol.cpp 1
+ This returns a pointer to a new instance of an ftp implementation
+ or null if no protocol for ftp was registered. The ownership of
+ the pointer is transferred to you, so you must delete it if you
+ don't need it anymore.
+
+ Normally you should not work directly with network protocols, so
+ you will not need to call this method yourself. Instead, use
+ Q3UrlOperator, which makes working with network protocols much more
+ convenient.
+
+ \sa Q3UrlOperator
+*/
+
+Q3NetworkProtocol *Q3NetworkProtocol::getNetworkProtocol( const QString &protocol )
+{
+ if ( !q3networkProtocolRegister ) {
+ q3networkProtocolRegister = new Q3NetworkProtocolDict;
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("file"), new Q3NetworkProtocolFactory< Q3LocalFs > );
+ }
+
+ if ( protocol.isNull() )
+ return 0;
+
+ Q3NetworkProtocolFactoryBase *factory = q3networkProtocolRegister->find( protocol );
+ if ( factory )
+ return factory->createObject();
+
+ return 0;
+}
+
+/*!
+ Returns true if the only protocol registered is for working on the
+ local filesystem; returns false if other network protocols are
+ also registered.
+*/
+
+bool Q3NetworkProtocol::hasOnlyLocalFileSystem()
+{
+ if ( !q3networkProtocolRegister )
+ return false;
+
+ Q3DictIterator< Q3NetworkProtocolFactoryBase > it( *q3networkProtocolRegister );
+ for ( ; it.current(); ++it )
+ if ( it.currentKey() != QLatin1String("file") )
+ return false;
+ return true;
+}
+
+/*!
+ \internal
+ Starts processing network operations.
+*/
+
+void Q3NetworkProtocol::startOps()
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: start processing operations" );
+#endif
+ processNextOperation( 0 );
+}
+
+/*!
+ \internal
+ Processes the operation \a op. It calls the
+ corresponding operation[something]( Q3NetworkOperation * )
+ methods.
+*/
+
+void Q3NetworkProtocol::processOperation( Q3NetworkOperation *op )
+{
+ if ( !op )
+ return;
+
+ switch ( op->operation() ) {
+ case OpListChildren:
+ operationListChildren( op );
+ break;
+ case OpMkDir:
+ operationMkDir( op );
+ break;
+ case OpRemove:
+ operationRemove( op );
+ break;
+ case OpRename:
+ operationRename( op );
+ break;
+ case OpGet:
+ operationGet( op );
+ break;
+ case OpPut:
+ operationPut( op );
+ break;
+ }
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports listing children (files);
+ this method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationListChildren( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports making directories; this
+ method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationMkDir( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports removing children (files);
+ this method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationRemove( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports renaming children (files);
+ this method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationRename( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports getting data; this method
+ should then process the Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationGet( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports putting (uploading) data;
+ this method should then process the Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationPut( Q3NetworkOperation * )
+{
+}
+
+/*! \internal
+*/
+
+void Q3NetworkProtocol::operationPutChunk( Q3NetworkOperation * )
+{
+}
+
+/*!
+ \internal
+ Handles operations. Deletes the previous operation object and
+ tries to process the next operation. It also checks the connection state
+ and only processes the next operation, if the connection of the protocol
+ is open. Otherwise it waits until the protocol opens the connection.
+*/
+
+void Q3NetworkProtocol::processNextOperation( Q3NetworkOperation *old )
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: process next operation, old: %p", old );
+#endif
+ d->removeTimer->stop();
+
+ if ( old )
+ d->oldOps.append( old );
+ if ( d->opInProgress && d->opInProgress!=old )
+ d->oldOps.append( d->opInProgress );
+
+ if ( d->operationQueue.isEmpty() ) {
+ d->opInProgress = 0;
+ if ( d->autoDelete )
+ d->removeTimer->start( d->removeInterval, true );
+ return;
+ }
+
+ Q3NetworkOperation *op = d->operationQueue.head();
+
+ d->opInProgress = op;
+
+ if ( !checkConnection( op ) ) {
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ d->opStartTimer->start( 0, true );
+ } else {
+ d->operationQueue.dequeue();
+ clearOperationQueue();
+ emit finished( op );
+ }
+
+ return;
+ }
+
+ d->opInProgress = op;
+ d->operationQueue.dequeue();
+ processOperation( op );
+}
+
+/*!
+ Returns the Q3UrlOperator on which the protocol works.
+*/
+
+Q3UrlOperator *Q3NetworkProtocol::url() const
+{
+ return d->url;
+}
+
+/*!
+ Returns the operation, which is being processed, or 0 of no
+ operation is being processed at the moment.
+*/
+
+Q3NetworkOperation *Q3NetworkProtocol::operationInProgress() const
+{
+ return d->opInProgress;
+}
+
+/*!
+ Clears the operation queue.
+*/
+
+void Q3NetworkProtocol::clearOperationQueue()
+{
+ d->operationQueue.dequeue();
+ d->operationQueue.setAutoDelete( true );
+ d->operationQueue.clear();
+}
+
+/*!
+ Stops the current operation that is being processed and clears all
+ waiting operations.
+*/
+
+void Q3NetworkProtocol::stop()
+{
+ Q3NetworkOperation *op = d->opInProgress;
+ clearOperationQueue();
+ if ( op ) {
+ op->setState( StStopped );
+ op->setProtocolDetail( tr( "Operation stopped by the user" ) );
+ emit finished( op );
+ setUrl( 0 );
+ op->free();
+ }
+}
+
+/*!
+ Because it's sometimes hard to take care of removing network
+ protocol instances, Q3NetworkProtocol provides an auto-delete
+ mechanism. If you set \a b to true, the network protocol instance
+ is removed after it has been inactive for \a i milliseconds (i.e.
+ \a i milliseconds after the last operation has been processed).
+ If you set \a b to false the auto-delete mechanism is switched
+ off.
+
+ If you switch on auto-delete, the Q3NetworkProtocol also deletes
+ its Q3UrlOperator.
+*/
+
+void Q3NetworkProtocol::setAutoDelete( bool b, int i )
+{
+ d->autoDelete = b;
+ d->removeInterval = i;
+}
+
+/*!
+ Returns true if auto-deleting is enabled; otherwise returns false.
+
+ \sa Q3NetworkProtocol::setAutoDelete()
+*/
+
+bool Q3NetworkProtocol::autoDelete() const
+{
+ return d->autoDelete;
+}
+
+/*!
+ \internal
+*/
+
+void Q3NetworkProtocol::removeMe()
+{
+ if ( d->autoDelete ) {
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: autodelete of Q3NetworkProtocol %p", this );
+#endif
+ delete d->url; // destructor deletes the network protocol
+ }
+}
+
+void Q3NetworkProtocol::emitNewChildren( const QUrlInfo &i, Q3NetworkOperation *op )
+{
+ Q3ValueList<QUrlInfo> lst;
+ lst << i;
+ emit newChildren( lst, op );
+}
+
+class Q3NetworkOperationPrivate
+{
+public:
+ Q3NetworkProtocol::Operation operation;
+ Q3NetworkProtocol::State state;
+ QMap<int, QString> args;
+ QMap<int, QByteArray> rawArgs;
+ QString protocolDetail;
+ int errorCode;
+ QTimer *deleteTimer;
+};
+
+/*!
+ \class Q3NetworkOperation
+
+ \brief The Q3NetworkOperation class provides common operations for network protocols.
+
+ \compat
+
+ An object is created to describe the operation and the current
+ state for each operation that a network protocol should process.
+
+ \sa Q3NetworkProtocol
+*/
+
+/*!
+ Constructs a network operation object. \a operation is the type of
+ the operation, and \a arg0, \a arg1 and \a arg2 are the first
+ three arguments of the operation. The state is initialized to
+ Q3NetworkProtocol::StWaiting.
+
+ \sa Q3NetworkProtocol::Operation Q3NetworkProtocol::State
+*/
+
+Q3NetworkOperation::Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QString &arg0, const QString &arg1,
+ const QString &arg2 )
+{
+ d = new Q3NetworkOperationPrivate;
+ d->deleteTimer = new QTimer( this );
+ connect( d->deleteTimer, SIGNAL(timeout()),
+ this, SLOT(deleteMe()) );
+ d->operation = operation;
+ d->state = Q3NetworkProtocol::StWaiting;
+ d->args[ 0 ] = arg0;
+ d->args[ 1 ] = arg1;
+ d->args[ 2 ] = arg2;
+ d->rawArgs[ 0 ] = QByteArray( 0 );
+ d->rawArgs[ 1 ] = QByteArray( 0 );
+ d->rawArgs[ 2 ] = QByteArray( 0 );
+ d->protocolDetail.clear();
+ d->errorCode = (int)Q3NetworkProtocol::NoError;
+}
+
+/*!
+ Constructs a network operation object. \a operation is the type of
+ the operation, and \a arg0, \a arg1 and \a arg2 are the first
+ three raw data arguments of the operation. The state is
+ initialized to Q3NetworkProtocol::StWaiting.
+
+ \sa Q3NetworkProtocol::Operation Q3NetworkProtocol::State
+*/
+
+Q3NetworkOperation::Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QByteArray &arg0, const QByteArray &arg1,
+ const QByteArray &arg2 )
+{
+ d = new Q3NetworkOperationPrivate;
+ d->deleteTimer = new QTimer( this );
+ connect( d->deleteTimer, SIGNAL(timeout()),
+ this, SLOT(deleteMe()) );
+ d->operation = operation;
+ d->state = Q3NetworkProtocol::StWaiting;
+ d->args[ 0 ].clear();
+ d->args[ 1 ].clear();
+ d->args[ 2 ].clear();
+ d->rawArgs[ 0 ] = arg0;
+ d->rawArgs[ 1 ] = arg1;
+ d->rawArgs[ 2 ] = arg2;
+ d->protocolDetail.clear();
+ d->errorCode = (int)Q3NetworkProtocol::NoError;
+}
+
+/*!
+ Destructor.
+*/
+
+Q3NetworkOperation::~Q3NetworkOperation()
+{
+ qDeleteInEventHandler(d->deleteTimer);
+ delete d;
+}
+
+/*!
+ Sets the \a state of the operation object. This should be done by
+ the network protocol during processing; at the end it should be
+ set to Q3NetworkProtocol::StDone or Q3NetworkProtocol::StFailed,
+ depending on success or failure.
+
+ \sa Q3NetworkProtocol::State
+*/
+
+void Q3NetworkOperation::setState( Q3NetworkProtocol::State state )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->state = state;
+}
+
+/*!
+ If the operation failed, the error message can be specified as \a
+ detail.
+*/
+
+void Q3NetworkOperation::setProtocolDetail( const QString &detail )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->protocolDetail = detail;
+}
+
+/*!
+ Sets the error code to \a ec.
+
+ If the operation failed, the protocol should set an error code to
+ describe the error in more detail. If possible, one of the error
+ codes defined in Q3NetworkProtocol should be used.
+
+ \sa setProtocolDetail() Q3NetworkProtocol::Error
+*/
+
+void Q3NetworkOperation::setErrorCode( int ec )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->errorCode = ec;
+}
+
+/*!
+ Sets the network operation's \a{num}-th argument to \a arg.
+*/
+
+void Q3NetworkOperation::setArg( int num, const QString &arg )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->args[ num ] = arg;
+}
+
+/*!
+ Sets the network operation's \a{num}-th raw data argument to \a arg.
+*/
+
+void Q3NetworkOperation::setRawArg( int num, const QByteArray &arg )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->rawArgs[ num ] = arg;
+}
+
+/*!
+ Returns the type of the operation.
+*/
+
+Q3NetworkProtocol::Operation Q3NetworkOperation::operation() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->operation;
+}
+
+/*!
+ Returns the state of the operation. You can determine whether an
+ operation is still waiting to be processed, is being processed,
+ has been processed successfully, or failed.
+*/
+
+Q3NetworkProtocol::State Q3NetworkOperation::state() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->state;
+}
+
+/*!
+ Returns the operation's \a{num}-th argument. If this argument was
+ not already set, an empty string is returned.
+*/
+
+QString Q3NetworkOperation::arg( int num ) const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->args[ num ];
+}
+
+/*!
+ Returns the operation's \a{num}-th raw data argument. If this
+ argument was not already set, an empty bytearray is returned.
+*/
+
+QByteArray Q3NetworkOperation::rawArg( int num ) const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->rawArgs[ num ];
+}
+
+/*!
+ Returns a detailed error message for the last error. This must
+ have been set using setProtocolDetail().
+*/
+
+QString Q3NetworkOperation::protocolDetail() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->protocolDetail;
+}
+
+/*!
+ Returns the error code for the last error that occurred.
+*/
+
+int Q3NetworkOperation::errorCode() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->errorCode;
+}
+
+/*!
+ \internal
+*/
+
+QByteArray& Q3NetworkOperation::raw( int num ) const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->rawArgs[ num ];
+}
+
+/*!
+ Sets this object to delete itself when it hasn't been used for one
+ second.
+
+ Because Q3NetworkOperation pointers are passed around a lot the
+ Q3NetworkProtocol generally does not have enough knowledge to
+ delete these at the correct time. If a Q3NetworkProtocol doesn't
+ need an operation any more it will call this function instead.
+
+ Note: you should never need to call the method yourself.
+*/
+
+void Q3NetworkOperation::free()
+{
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+}
+
+/*!
+ \internal
+ Internal slot for auto-deletion.
+*/
+
+void Q3NetworkOperation::deleteMe()
+{
+ delete this;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_q3networkprotocol.cpp"
+
+#endif
diff --git a/src/qt3support/network/q3networkprotocol.h b/src/qt3support/network/q3networkprotocol.h
new file mode 100644
index 0000000..87ca10c
--- /dev/null
+++ b/src/qt3support/network/q3networkprotocol.h
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3NETWORKPROTOCOL_H
+#define Q3NETWORKPROTOCOL_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qobject.h>
+#include <Qt3Support/q3dict.h>
+#include <Qt3Support/q3valuelist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+class Q3NetworkProtocol;
+class Q3NetworkOperation;
+class QTimer;
+class Q3UrlOperator;
+class Q3NetworkProtocolPrivate;
+class QUrlInfo;
+template <class T> class Q3ValueList;
+
+class Q_COMPAT_EXPORT Q3NetworkProtocolFactoryBase
+{
+public:
+ virtual ~Q3NetworkProtocolFactoryBase() {}
+ virtual Q3NetworkProtocol *createObject() = 0;
+};
+
+template< class T >
+class Q3NetworkProtocolFactory : public Q3NetworkProtocolFactoryBase
+{
+public:
+ Q3NetworkProtocol *createObject() {
+ return new T;
+ }
+
+};
+
+typedef Q3Dict< Q3NetworkProtocolFactoryBase > Q3NetworkProtocolDict;
+
+class Q_COMPAT_EXPORT Q3NetworkProtocol : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum State {
+ StWaiting = 0,
+ StInProgress,
+ StDone,
+ StFailed,
+ StStopped
+ };
+
+ enum Operation {
+ OpListChildren = 1,
+ OpMkDir = 2,
+ OpMkdir = OpMkDir, // ### remove in 4.0
+ OpRemove = 4,
+ OpRename = 8,
+ OpGet = 32,
+ OpPut = 64
+ };
+
+ enum ConnectionState {
+ ConHostFound,
+ ConConnected,
+ ConClosed
+ };
+
+ enum Error {
+ // no error
+ NoError = 0,
+ // general errors
+ ErrValid,
+ ErrUnknownProtocol,
+ ErrUnsupported,
+ ErrParse,
+ // errors on connect
+ ErrLoginIncorrect,
+ ErrHostNotFound,
+ // protocol errors
+ ErrListChildren,
+ ErrListChlidren = ErrListChildren, // ### remove in 4.0
+ ErrMkDir,
+ ErrMkdir = ErrMkDir, // ### remove in 4.0
+ ErrRemove,
+ ErrRename,
+ ErrGet,
+ ErrPut,
+ ErrFileNotExisting,
+ ErrPermissionDenied
+ };
+
+ Q3NetworkProtocol();
+ virtual ~Q3NetworkProtocol();
+
+ virtual void setUrl( Q3UrlOperator *u );
+
+ virtual void setAutoDelete( bool b, int i = 10000 );
+ bool autoDelete() const;
+
+ static void registerNetworkProtocol( const QString &protocol,
+ Q3NetworkProtocolFactoryBase *protocolFactory );
+ static Q3NetworkProtocol *getNetworkProtocol( const QString &protocol );
+ static bool hasOnlyLocalFileSystem();
+
+ virtual int supportedOperations() const;
+ virtual void addOperation( Q3NetworkOperation *op );
+
+ Q3UrlOperator *url() const;
+ Q3NetworkOperation *operationInProgress() const;
+ virtual void clearOperationQueue();
+ virtual void stop();
+
+Q_SIGNALS:
+ void data( const QByteArray &, Q3NetworkOperation *res );
+ void connectionStateChanged( int state, const QString &data );
+ void finished( Q3NetworkOperation *res );
+ void start( Q3NetworkOperation *res );
+ void newChildren( const Q3ValueList<QUrlInfo> &, Q3NetworkOperation *res );
+ void newChild( const QUrlInfo &, Q3NetworkOperation *res );
+ void createdDirectory( const QUrlInfo &, Q3NetworkOperation *res );
+ void removed( Q3NetworkOperation *res );
+ void itemChanged( Q3NetworkOperation *res );
+ void dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *res );
+
+protected:
+ virtual void processOperation( Q3NetworkOperation *op );
+ virtual void operationListChildren( Q3NetworkOperation *op );
+ virtual void operationMkDir( Q3NetworkOperation *op );
+ virtual void operationRemove( Q3NetworkOperation *op );
+ virtual void operationRename( Q3NetworkOperation *op );
+ virtual void operationGet( Q3NetworkOperation *op );
+ virtual void operationPut( Q3NetworkOperation *op );
+ virtual void operationPutChunk( Q3NetworkOperation *op );
+ virtual bool checkConnection( Q3NetworkOperation *op );
+
+private:
+ Q3NetworkProtocolPrivate *d;
+
+private Q_SLOTS:
+ void processNextOperation( Q3NetworkOperation *old );
+ void startOps();
+ void emitNewChildren( const QUrlInfo &i, Q3NetworkOperation *op );
+
+ void removeMe();
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3NetworkProtocol( const Q3NetworkProtocol & );
+ Q3NetworkProtocol &operator=( const Q3NetworkProtocol & );
+#endif
+};
+
+class Q3NetworkOperationPrivate;
+
+class Q_COMPAT_EXPORT Q3NetworkOperation : public QObject
+{
+ Q_OBJECT
+ friend class Q3UrlOperator;
+
+public:
+ Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QString &arg0, const QString &arg1,
+ const QString &arg2 );
+ Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QByteArray &arg0, const QByteArray &arg1,
+ const QByteArray &arg2 );
+ ~Q3NetworkOperation();
+
+ void setState( Q3NetworkProtocol::State state );
+ void setProtocolDetail( const QString &detail );
+ void setErrorCode( int ec );
+ void setArg( int num, const QString &arg );
+ void setRawArg( int num, const QByteArray &arg );
+
+ Q3NetworkProtocol::Operation operation() const;
+ Q3NetworkProtocol::State state() const;
+ QString arg( int num ) const;
+ QByteArray rawArg( int num ) const;
+ QString protocolDetail() const;
+ int errorCode() const;
+
+ void free();
+
+private Q_SLOTS:
+ void deleteMe();
+
+private:
+ QByteArray &raw( int num ) const;
+ Q3NetworkOperationPrivate *d;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3NetworkOperation( const Q3NetworkOperation & );
+ Q3NetworkOperation &operator=( const Q3NetworkOperation & );
+#endif
+};
+
+#endif // QT_NO_NETWORKPROTOCOL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3NETWORKPROTOCOL_H
diff --git a/src/qt3support/network/q3serversocket.cpp b/src/qt3support/network/q3serversocket.cpp
new file mode 100644
index 0000000..f5b67d1
--- /dev/null
+++ b/src/qt3support/network/q3serversocket.cpp
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3serversocket.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "qsocketnotifier.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3ServerSocketPrivate {
+public:
+ Q3ServerSocketPrivate(): s(0), n(0) {}
+ ~Q3ServerSocketPrivate() { delete n; delete s; }
+ Q3SocketDevice *s;
+ QSocketNotifier *n;
+};
+
+
+/*!
+ \class Q3ServerSocket
+ \brief The Q3ServerSocket class provides a TCP-based server.
+
+ \compat
+
+ This class is a convenience class for accepting incoming TCP
+ connections. You can specify the port or have Q3ServerSocket pick
+ one, and listen on just one address or on all the machine's
+ addresses.
+
+ Using the API is very simple: subclass Q3ServerSocket, call the
+ constructor of your choice, and implement newConnection() to
+ handle new incoming connections. There is nothing more to do.
+
+ (Note that due to lack of support in the underlying APIs,
+ Q3ServerSocket cannot accept or reject connections conditionally.)
+
+ \sa Q3Socket, Q3SocketDevice, QHostAddress, QSocketNotifier
+*/
+
+
+/*!
+ Creates a server socket object, that will serve the given \a port
+ on all the addresses of this host. If \a port is 0, Q3ServerSocket
+ will pick a suitable port in a system-dependent manner. Use \a
+ backlog to specify how many pending connections the server can
+ have.
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+
+ \warning On Tru64 Unix systems a value of 0 for \a backlog means
+ that you don't accept any connections at all; you should specify a
+ value larger than 0.
+*/
+
+Q3ServerSocket::Q3ServerSocket( Q_UINT16 port, int backlog,
+ QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ d = new Q3ServerSocketPrivate;
+ init( QHostAddress(), port, backlog );
+}
+
+
+/*!
+ Creates a server socket object, that will serve the given \a port
+ only on the given \a address. Use \a backlog to specify how many
+ pending connections the server can have.
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+
+ \warning On Tru64 Unix systems a value of 0 for \a backlog means
+ that you don't accept any connections at all; you should specify a
+ value larger than 0.
+*/
+
+Q3ServerSocket::Q3ServerSocket( const QHostAddress & address, Q_UINT16 port,
+ int backlog,
+ QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ d = new Q3ServerSocketPrivate;
+ init( address, port, backlog );
+}
+
+
+/*!
+ Construct an empty server socket.
+
+ This constructor, in combination with setSocket(), allows us to
+ use the Q3ServerSocket class as a wrapper for other socket types
+ (e.g. Unix Domain Sockets under Unix).
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+
+ \sa setSocket()
+*/
+
+Q3ServerSocket::Q3ServerSocket( QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ d = new Q3ServerSocketPrivate;
+}
+
+
+/*!
+ Returns true if the construction succeeded; otherwise returns false.
+*/
+bool Q3ServerSocket::ok() const
+{
+ return !!d->s;
+}
+
+/*
+ The common bit of the constructors.
+ */
+void Q3ServerSocket::init( const QHostAddress & address, Q_UINT16 port, int backlog )
+{
+ d->s = new Q3SocketDevice( Q3SocketDevice::Stream, address.isIPv4Address()
+ ? Q3SocketDevice::IPv4 : Q3SocketDevice::IPv6, 0 );
+#if !defined(Q_OS_WIN32)
+ // Under Unix, we want to be able to use the port, even if a socket on the
+ // same address-port is in TIME_WAIT. Under Windows this is possible anyway
+ // -- furthermore, the meaning of reusable is different: it means that you
+ // can use the same address-port for multiple listening sockets.
+ d->s->setAddressReusable( true );
+#endif
+ if ( d->s->bind( address, port )
+ && d->s->listen( backlog ) )
+ {
+ d->n = new QSocketNotifier( d->s->socket(), QSocketNotifier::Read,
+ this, "accepting new connections" );
+ connect( d->n, SIGNAL(activated(int)),
+ this, SLOT(incomingConnection(int)) );
+ } else {
+ qWarning( "Q3ServerSocket: failed to bind or listen to the socket" );
+ delete d->s;
+ d->s = 0;
+ }
+}
+
+
+/*!
+ Destroys the socket.
+
+ This causes any backlogged connections (connections that have
+ reached the host, but not yet been completely set up by calling
+ Q3SocketDevice::accept()) to be severed.
+
+ Existing connections continue to exist; this only affects the
+ acceptance of new connections.
+*/
+Q3ServerSocket::~Q3ServerSocket()
+{
+ delete d;
+}
+
+
+/*!
+ \fn void Q3ServerSocket::newConnection( int socket )
+
+ This pure virtual function is responsible for setting up a new
+ incoming connection. \a socket is the fd (file descriptor) for the
+ newly accepted connection.
+*/
+
+
+void Q3ServerSocket::incomingConnection( int )
+{
+ int fd = d->s->accept();
+ if ( fd >= 0 )
+ newConnection( fd );
+}
+
+
+/*!
+ Returns the port number on which this server socket listens. This
+ is always non-zero; if you specify 0 in the constructor,
+ Q3ServerSocket will pick a non-zero port itself. ok() must be true
+ before calling this function.
+
+ \sa address() Q3SocketDevice::port()
+*/
+Q_UINT16 Q3ServerSocket::port() const
+{
+ if ( !d || !d->s )
+ return 0;
+ return d->s->port();
+}
+
+
+/*!
+ Returns the operating system socket.
+*/
+int Q3ServerSocket::socket() const
+{
+ if ( !d || !d->s )
+ return -1;
+
+ return d->s->socket();
+}
+
+/*!
+ Returns the address on which this object listens, or 0.0.0.0 if
+ this object listens on more than one address. ok() must be true
+ before calling this function.
+
+ \sa port() Q3SocketDevice::address()
+*/
+QHostAddress Q3ServerSocket::address() const
+{
+ if ( !d || !d->s )
+ return QHostAddress();
+
+ return d->s->address();
+}
+
+
+/*!
+ Returns a pointer to the internal socket device. The returned
+ pointer is 0 if there is no connection or pending connection.
+
+ There is normally no need to manipulate the socket device directly
+ since this class does all the necessary setup for most client or
+ server socket applications.
+*/
+Q3SocketDevice *Q3ServerSocket::socketDevice()
+{
+ if ( !d )
+ return 0;
+
+ return d->s;
+}
+
+
+/*!
+ Sets the socket to use \a socket. bind() and listen() should
+ already have been called for \a socket.
+
+ This allows us to use the Q3ServerSocket class as a wrapper for
+ other socket types (e.g. Unix Domain Sockets).
+*/
+void Q3ServerSocket::setSocket( int socket )
+{
+ delete d;
+ d = new Q3ServerSocketPrivate;
+ d->s = new Q3SocketDevice( socket, Q3SocketDevice::Stream );
+ d->n = new QSocketNotifier( d->s->socket(), QSocketNotifier::Read,
+ this, "accepting new connections" );
+ connect( d->n, SIGNAL(activated(int)),
+ this, SLOT(incomingConnection(int)) );
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3serversocket.h b/src/qt3support/network/q3serversocket.h
new file mode 100644
index 0000000..5866fad
--- /dev/null
+++ b/src/qt3support/network/q3serversocket.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SERVERSOCKET_H
+#define Q3SERVERSOCKET_H
+
+#include <QtCore/qobject.h>
+#include <QtNetwork/qhostaddress.h>
+#include <Qt3Support/q3socketdevice.h> // ### remove or keep for users' convenience?
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+class Q3ServerSocketPrivate;
+
+class Q_COMPAT_EXPORT Q3ServerSocket : public QObject
+{
+ Q_OBJECT
+public:
+ Q3ServerSocket( Q_UINT16 port, int backlog = 1,
+ QObject *parent=0, const char *name=0 );
+ Q3ServerSocket( const QHostAddress & address, Q_UINT16 port, int backlog = 1,
+ QObject *parent=0, const char *name=0 );
+ Q3ServerSocket( QObject *parent=0, const char *name=0 );
+ virtual ~Q3ServerSocket();
+
+ bool ok() const;
+
+ Q_UINT16 port() const ;
+
+ int socket() const ;
+ virtual void setSocket( int socket );
+
+ QHostAddress address() const ;
+
+ virtual void newConnection( int socket ) = 0;
+
+protected:
+ Q3SocketDevice *socketDevice();
+
+private Q_SLOTS:
+ void incomingConnection( int socket );
+
+private:
+ Q3ServerSocketPrivate *d;
+ void init( const QHostAddress & address, Q_UINT16 port, int backlog );
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SERVERSOCKET_H
diff --git a/src/qt3support/network/q3socket.cpp b/src/qt3support/network/q3socket.cpp
new file mode 100644
index 0000000..7ca4051
--- /dev/null
+++ b/src/qt3support/network/q3socket.cpp
@@ -0,0 +1,1518 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3socket.h"
+#ifndef QT_NO_NETWORK
+#include "q3ptrlist.h"
+#include "qtimer.h"
+#include "q3socketdevice.h"
+#include "q3dns.h"
+#include "private/q3membuf_p.h"
+
+#include <string.h>
+#ifndef NO_ERRNO_H
+#if defined(Q_OS_WINCE)
+#include "qfunctions_wince.h"
+#else
+#include <errno.h>
+#endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3SOCKET_DEBUG
+
+/*
+ Perhaps this private functionality needs to be refactored.
+
+ Comment from Robert D Gatlin (Intel):
+
+ It would be nice to have the functionality inherent in Q3Socket available
+ as a separate class as a standard part of the Qt library, something along
+ the line of:
+
+ class QByteBuffer : public QIODevice { ... }
+
+ The same class could/would be used within Q3Socket for the Read/Write
+ buffers.
+
+ The above class could be used in the following way(s):
+
+ buffer.open( IO_WriteOnly | IO_Append );
+ buffer.writeBlock( a ); // a = QByteArray
+ buffer.close();
+
+ QByteArray b;
+ b.resize( buffer.size() );
+ buffer.open( IO_ReadOnly );
+ buffer.readBlock( b.data(), b.size() );
+ buffer.close();
+
+ But would also be useable with QDataStream (via QIODevice) with:
+
+ buffer.open( IO_WriteOnly | IO_Append );
+ QDataStream is( &buffer );
+ is << 100;
+ buffer.close();
+
+ buffer.open( IO_ReadOnly );
+ QDataStream os( &buffer );
+ Q_UINT32 x;
+ os >> x;
+ buffer.close();
+
+ The real usefulness is with any situations where data (QByteArray) arrives
+ incrementally (as in Q3Socket and filter case above).
+
+ I tried using QBuffer, but QBuffer does not trim bytes from the front of
+ the buffer in cases like:
+
+ QBuffer buf;
+ buf.open( IO_ReadOnly );
+ QDataStream ds( &buf );
+ Q_INT32 x;
+ ds >> x;
+ buf.close();
+
+ In the above case, buf.size() will be identical before and after the
+ operation with QDataStream. Based on the implementation of QBuffer, it
+ does not appear well suited for this kind of operation.
+*/
+
+// Private class for Q3Socket
+
+class Q3SocketPrivate {
+public:
+ Q3SocketPrivate();
+ ~Q3SocketPrivate();
+ void closeSocket();
+ void close();
+ void connectionClosed();
+ void setSocketDevice( Q3Socket *q, Q3SocketDevice *device );
+
+ Q3Socket::State state; // connection state
+ QString host; // host name
+ Q_UINT16 port; // host port
+ Q3SocketDevice *socket; // connection socket
+ QSocketNotifier *rsn, *wsn; // socket notifiers
+ Q3Membuf rba; // read buffer
+ Q_ULONG readBufferSize; // limit for the read buffer size
+ Q3PtrList<QByteArray> wba; // list of write bufs
+ QHostAddress addr; // connection address
+ Q3ValueList<QHostAddress> addresses; // alternatives looked up
+ QIODevice::Offset wsize; // write total buf size
+ QIODevice::Offset windex; // write index
+#ifndef QT_NO_DNS
+ Q3Dns *dns4;
+ Q3Dns *dns6;
+#endif
+ static Q3PtrList<Q3Socket> sn_read_alreadyCalled; // used to avoid unwanted recursion
+ Q3ValueList<QHostAddress> l4;
+ Q3ValueList<QHostAddress> l6;
+};
+
+Q3PtrList<Q3Socket> Q3SocketPrivate::sn_read_alreadyCalled;
+
+Q3SocketPrivate::Q3SocketPrivate()
+ : state(Q3Socket::Idle), host(QString::fromLatin1("")), port(0),
+ socket(0), rsn(0), wsn(0), readBufferSize(0), wsize(0), windex(0)
+{
+#ifndef QT_NO_DNS
+ dns4 = 0;
+ dns6 = 0;
+#endif
+ wba.setAutoDelete( true );
+}
+
+Q3SocketPrivate::~Q3SocketPrivate()
+{
+ close();
+ delete socket;
+#ifndef QT_NO_DNS
+ delete dns4;
+ delete dns6;
+#endif
+}
+
+extern void qDeleteInEventHandler(QObject *o);
+void Q3SocketPrivate::closeSocket()
+{
+ // Order is important here - the socket notifiers must go away
+ // before the socket does, otherwise libc or the kernel will
+ // become unhappy.
+ if (rsn) {
+ qDeleteInEventHandler(rsn);
+ rsn = 0;
+ }
+ if (wsn) {
+ qDeleteInEventHandler(wsn);
+ wsn = 0;
+ }
+ if ( socket )
+ socket->close();
+}
+
+void Q3SocketPrivate::close()
+{
+ closeSocket();
+ wsize = 0;
+ rba.clear(); wba.clear();
+ windex = 0;
+}
+
+void Q3SocketPrivate::connectionClosed()
+{
+ // We keep the open state in case there's unread incoming data
+ state = Q3Socket::Idle;
+ closeSocket();
+ wba.clear();
+ windex = wsize = 0;
+}
+
+void Q3SocketPrivate::setSocketDevice( Q3Socket *q, Q3SocketDevice *device )
+{
+ delete socket;
+ delete rsn;
+ delete wsn;
+
+ if ( device ) {
+ socket = device;
+ } else {
+ socket = new Q3SocketDevice( Q3SocketDevice::Stream,
+ ( addr.isIPv4Address() ?
+ Q3SocketDevice::IPv4 :
+ Q3SocketDevice::IPv6 ), 0 );
+ socket->setBlocking( false );
+ socket->setAddressReusable( true );
+ }
+
+ rsn = new QSocketNotifier( socket->socket(),
+ QSocketNotifier::Read, q, "read" );
+ wsn = new QSocketNotifier( socket->socket(),
+ QSocketNotifier::Write, q, "write" );
+
+ QObject::connect( rsn, SIGNAL(activated(int)), q, SLOT(sn_read()) );
+ rsn->setEnabled( false );
+ QObject::connect( wsn, SIGNAL(activated(int)), q, SLOT(sn_write()) );
+ wsn->setEnabled( false );
+}
+
+/*!
+ \class Q3Socket
+ \brief The Q3Socket class provides a buffered TCP connection.
+
+ \compat
+
+ It provides a totally non-blocking QIODevice, and modifies and
+ extends the API of QIODevice with socket-specific code.
+
+ The functions you're likely to call most are connectToHost(),
+ bytesAvailable(), canReadLine() and the ones it inherits from
+ QIODevice.
+
+ connectToHost() is the most-used function. As its name implies,
+ it opens a connection to a named host.
+
+ Most network protocols are either packet-oriented or
+ line-oriented. canReadLine() indicates whether a connection
+ contains an entire unread line or not, and bytesAvailable()
+ returns the number of bytes available for reading.
+
+ The signals error(), connected(), readyRead() and
+ connectionClosed() inform you of the progress of the connection.
+ There are also some less commonly used signals. hostFound() is
+ emitted when connectToHost() has finished its DNS lookup and is
+ starting its TCP connection. delayedCloseFinished() is emitted
+ when close() succeeds. bytesWritten() is emitted when Q3Socket
+ moves data from its "to be written" queue into the TCP
+ implementation.
+
+ There are several access functions for the socket: state() returns
+ whether the object is idle, is doing a DNS lookup, is connecting,
+ has an operational connection, etc. address() and port() return
+ the IP address and port used for the connection. The peerAddress()
+ and peerPort() functions return the IP address and port used by
+ the peer, and peerName() returns the name of the peer (normally
+ the name that was passed to connectToHost()). socketDevice()
+ returns a pointer to the Q3SocketDevice used for this socket.
+
+ Q3Socket inherits QIODevice, and reimplements some functions. In
+ general, you can treat it as a QIODevice for writing, and mostly
+ also for reading. The match isn't perfect, since the QIODevice
+ API is designed for devices that are controlled by the same
+ machine, and an asynchronous peer-to-peer network connection isn't
+ quite like that. For example, there is nothing that matches
+ QIODevice::size() exactly. The documentation for open(), close(),
+ flush(), size(), at(), atEnd(), readBlock(), writeBlock(),
+ getch(), putch(), ungetch() and readLine() describes the
+ differences in detail.
+
+ \warning Q3Socket is not suitable for use in threads. If you need
+ to uses sockets in threads use the lower-level Q3SocketDevice class.
+
+ \sa Q3SocketDevice, QHostAddress, QSocketNotifier
+*/
+
+
+/*!
+ Creates a Q3Socket object in Q3Socket::Idle state.
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+*/
+
+Q3Socket::Q3Socket( QObject *parent, const char *name )
+ : QIODevice( parent )
+{
+ setObjectName(QLatin1String(name));
+ d = new Q3SocketPrivate;
+ setSocketDevice( 0 );
+ resetStatus();
+}
+
+
+/*!
+ Destroys the socket. Closes the connection if necessary.
+
+ \sa close()
+*/
+
+Q3Socket::~Q3Socket()
+{
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): Destroy", name() );
+#endif
+ if ( state() != Idle )
+ close();
+ Q_ASSERT( d != 0 );
+ delete d;
+}
+
+
+/*!
+ Returns a pointer to the internal socket device.
+
+ There is normally no need to manipulate the socket device directly
+ since this class does the necessary setup for most applications.
+*/
+
+Q3SocketDevice *Q3Socket::socketDevice()
+{
+ return d->socket;
+}
+
+/*!
+ Sets the internal socket device to \a device. Passing a \a device
+ of 0 will cause the internal socket device to be used. Any
+ existing connection will be disconnected before using the new \a
+ device.
+
+ The new device should not be connected before being associated
+ with a Q3Socket; after setting the socket call connectToHost() to
+ make the connection.
+
+ This function is useful if you need to subclass Q3SocketDevice and
+ want to use the Q3Socket API, for example, to implement Unix domain
+ sockets.
+*/
+
+void Q3Socket::setSocketDevice( Q3SocketDevice *device )
+{
+ if ( state() != Idle )
+ close();
+ d->setSocketDevice( this, device );
+}
+
+/*!
+ \enum Q3Socket::State
+
+ This enum defines the connection states:
+
+ \value Idle if there is no connection
+ \value HostLookup during a DNS lookup
+ \value Connecting during TCP connection establishment
+ \value Connected when there is an operational connection
+ \value Closing if the socket is closing down, but is not yet closed.
+ \omitvalue Connection
+*/
+
+/*!
+ Returns the current state of the socket connection.
+
+ \sa Q3Socket::State
+*/
+
+Q3Socket::State Q3Socket::state() const
+{
+ return d->state;
+}
+
+
+#ifndef QT_NO_DNS
+
+/*!
+ Attempts to make a connection to \a host on the specified \a port
+ and return immediately.
+
+ Any connection or pending connection is closed immediately, and
+ Q3Socket goes into the \c HostLookup state. When the lookup
+ succeeds, it emits hostFound(), starts a TCP connection and goes
+ into the \c Connecting state. Finally, when the connection
+ succeeds, it emits connected() and goes into the \c Connected
+ state. If there is an error at any point, it emits error().
+
+ \a host may be an IP address in string form, or it may be a DNS
+ name. Q3Socket will do a normal DNS lookup if required. Note that
+ \a port is in native byte order, unlike some other libraries.
+
+ \sa state()
+*/
+
+void Q3Socket::connectToHost( const QString &host, Q_UINT16 port )
+{
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::connectToHost: host %s, port %d",
+ name(), host.ascii(), port );
+#endif
+ setSocketIntern( -1 );
+ d->state = HostLookup;
+ d->host = host;
+ d->port = port;
+ d->dns4 = new Q3Dns( host, Q3Dns::A );
+ d->dns6 = new Q3Dns( host, Q3Dns::Aaaa );
+
+ // try if the address is already available (for faster connecting...)
+ tryConnecting();
+ if ( d->state == HostLookup ) {
+ connect( d->dns4, SIGNAL(resultsReady()),
+ this, SLOT(tryConnecting()) );
+ connect( d->dns6, SIGNAL(resultsReady()),
+ this, SLOT(tryConnecting()) );
+ }
+}
+
+#endif
+
+
+/*!
+ This private slots continues the connection process where
+ connectToHost() leaves off.
+*/
+
+void Q3Socket::tryConnecting()
+{
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting()", name() );
+#endif
+ // ### this ifdef isn't correct - addresses() also does /etc/hosts and
+ // numeric-address-as-string handling.
+#ifndef QT_NO_DNS
+
+ if ( d->dns4 ) {
+ d->l4 = d->dns4->addresses();
+ if ( !d->l4.isEmpty() || !d->dns4->isWorking() ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: host %s, port %d: "
+ "%d IPv4 addresses",
+ name(), d->host.ascii(), d->port, d->l4.count() );
+#endif
+ delete d->dns4;
+ d->dns4 = 0;
+ }
+ }
+
+ if ( d->dns6 ) {
+ d->l6 = d->dns6->addresses();
+ if ( !d->l6.isEmpty() || !d->dns6->isWorking() ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: host %s, port %d: "
+ "%d IPv6 addresses",
+ name(), d->host.ascii(), d->port, d->l6.count() );
+#endif
+ delete d->dns6;
+ d->dns6 = 0;
+ }
+ }
+
+ if ( d->state == HostLookup ) {
+ if ( d->l4.isEmpty() && d->l6.isEmpty() &&
+ !d->dns4 && !d->dns6 ) {
+ // no results and we're not still looking: give up
+ d->state = Idle;
+ emit error( ErrHostNotFound );
+ return;
+ }
+ if ( d->l4.isEmpty() && d->l6.isEmpty() ) {
+ // no results (yet): try again later
+ return;
+ }
+
+ // we've found something. press on with that. if we later find
+ // more, fine.
+ emit hostFound();
+ d->state = Connecting;
+ }
+
+ if ( d->state == Connecting ) {
+ d->addresses += d->l4;
+ d->addresses += d->l6;
+ d->l4.clear();
+ d->l6.clear();
+
+ // try one address at a time, falling back to the next one if
+ // there is a connection failure. (should also support a timeout,
+ // or do multiple TCP-level connects at a time, with staggered
+ // starts to avoid bandwidth waste and cause fewer
+ // "connect-and-abort" errors. but that later.)
+ bool stuck = true;
+ while( stuck ) {
+ stuck = false;
+ if ( d->socket &&
+ d->socket->connect( d->addr, d->port ) == false ) {
+ if ( d->socket->error() == Q3SocketDevice::NoError ) {
+ if ( d->wsn )
+ d->wsn->setEnabled( true );
+ return; // not serious, try again later
+ }
+
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: "
+ "Gave up on IP address %s",
+ name(), d->socket->peerAddress().toString().ascii() );
+#endif
+ delete d->wsn;
+ d->wsn = 0;
+ delete d->rsn;
+ d->rsn = 0;
+ delete d->socket;
+ d->socket = 0;
+
+ if(d->addresses.isEmpty()) {
+ emit error( ErrConnectionRefused );
+ return;
+ }
+ }
+ // if the host has more addresses, try another some.
+ if ( d->socket == 0 && !d->addresses.isEmpty() ) {
+ d->addr = *d->addresses.begin();
+ d->addresses.remove( d->addresses.begin() );
+ d->setSocketDevice( this, 0 );
+ stuck = true;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: Trying IP address %s",
+ name(), d->addr.toString().ascii() );
+#endif
+ }
+ };
+
+ // The socket write notifier will fire when the connection succeeds
+ if ( d->wsn )
+ d->wsn->setEnabled( true );
+ }
+#endif
+}
+
+/*!
+ \enum Q3Socket::Error
+
+ This enum specifies the possible errors:
+ \value ErrConnectionRefused if the connection was refused
+ \value ErrHostNotFound if the host was not found
+ \value ErrSocketRead if a read from the socket failed
+*/
+
+/*!
+ \fn void Q3Socket::error(int error)
+
+ This signal is emitted after an error occurred. The \a error parameter is
+ the \l Error value.
+*/
+
+/*!
+ \fn void Q3Socket::hostFound()
+
+ This signal is emitted after connectToHost() has been called and
+ the host lookup has succeeded.
+
+ \sa connected()
+*/
+
+
+/*!
+ \fn void Q3Socket::connected()
+
+ This signal is emitted after connectToHost() has been called and a
+ connection has been successfully established.
+
+ \sa connectToHost(), connectionClosed()
+*/
+
+
+/*!
+ \fn void Q3Socket::connectionClosed()
+
+ This signal is emitted when the other end has closed the
+ connection. The read buffers may contain buffered input data which
+ you can read after the connection was closed.
+
+ \sa connectToHost(), close()
+*/
+
+
+/*!
+ \fn void Q3Socket::delayedCloseFinished()
+
+ This signal is emitted when a delayed close is finished.
+
+ If you call close() and there is buffered output data to be
+ written, Q3Socket goes into the Q3Socket::Closing state and
+ returns immediately. It will then keep writing to the socket until
+ all the data has been written. Then, the delayedCloseFinished()
+ signal is emitted.
+
+ \sa close()
+*/
+
+
+/*!
+ \fn void Q3Socket::readyRead()
+
+ This signal is emitted every time there is new incoming data.
+
+ Bear in mind that new incoming data is only reported once; if you do not
+ read all the data, this class buffers the data and you can read it later,
+ but no signal is emitted unless new data arrives. A good practice is to
+ read all data in the slot connected to this signal unless you are sure that
+ you need to receive more data to be able to process it.
+
+ \sa readBlock(), readLine(), bytesAvailable()
+*/
+
+
+/*!
+ \fn void Q3Socket::bytesWritten( int nbytes )
+
+ This signal is emitted when data has been written to the network.
+ The \a nbytes parameter specifies how many bytes were written.
+
+ The bytesToWrite() function is often used in the same context; it
+ indicates how many buffered bytes there are left to write.
+
+ \sa writeBlock(), bytesToWrite()
+*/
+
+
+/*!
+ Opens the socket using the specified QIODevice file mode \a m.
+ This function is called automatically when needed and you should
+ not call it yourself.
+
+ \sa close()
+*/
+
+bool Q3Socket::open( OpenMode m )
+{
+ if ( isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "Q3Socket::open: Already open" );
+#endif
+ return false;
+ }
+ QIODevice::setOpenMode( m & ReadWrite );
+ return true;
+}
+
+/*!
+ \fn bool Q3Socket::open(int m)
+ \overload
+*/
+
+/*!
+ Closes the socket.
+
+ The read buffer is cleared.
+
+ If the output buffer is empty, the state is set to \c
+ Q3Socket::Idle and the connection is terminated immediately. If the
+ output buffer still contains data to be written, Q3Socket goes into
+ the Q3Socket::Closing state and the rest of the data will be
+ written. When all of the outgoing data have been written, the
+ state is set to Q3Socket::Idle and the connection is terminated.
+ At this point, the delayedCloseFinished() signal is emitted.
+
+ If you don't want that the data of the output buffer is written, call
+ clearPendingData() before you call close().
+
+ \sa state(), bytesToWrite() clearPendingData()
+*/
+
+void Q3Socket::close()
+{
+ if ( !isOpen() || d->state == Idle ) // already closed
+ return;
+ if ( d->state == Closing )
+ return;
+ if ( !d->rsn || !d->wsn )
+ return;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): close socket", name() );
+#endif
+ if ( d->socket && d->wsize ) { // there's data to be written
+ d->state = Closing;
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ if ( d->wsn )
+ d->wsn->setEnabled( true );
+ d->rba.clear(); // clear incoming data
+ return;
+ }
+ resetStatus();
+ setOpenMode(NotOpen);
+ d->close();
+ d->state = Idle;
+}
+
+
+/*!
+ This function consumes \a nbytes bytes of data from the write
+ buffer.
+*/
+
+bool Q3Socket::consumeWriteBuf( Q_ULONG nbytes )
+{
+ if ( nbytes <= 0 || (qint64)nbytes > d->wsize )
+ return false;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): skipWriteBuf %d bytes", name(), (int)nbytes );
+#endif
+ d->wsize -= nbytes;
+ for ( ;; ) {
+ QByteArray *a = d->wba.first();
+ if ( (qint64)(d->windex + nbytes) >= a->size() ) {
+ nbytes -= a->size() - d->windex;
+ d->wba.remove();
+ d->windex = 0;
+ if ( nbytes == 0 )
+ break;
+ } else {
+ d->windex += nbytes;
+ break;
+ }
+ }
+ return true;
+}
+
+
+
+/*!
+ Implementation of the abstract virtual QIODevice::flush() function.
+ This function always returns true.
+*/
+
+bool Q3Socket::flush()
+{
+ if ( !d->socket )
+ return true;
+ bool osBufferFull = false;
+ int consumed = 0;
+ while ( !osBufferFull && d->state >= Connecting && d->wsize > 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): flush: Write data to the socket", name() );
+#endif
+ QByteArray *a = d->wba.first();
+ int nwritten;
+ int i = 0;
+ if ( (int)a->size() - d->windex < 1460 ) {
+ // Concatenate many smaller blocks. the first may be
+ // partial, but each subsequent block is copied entirely
+ // or not at all. the sizes here are picked so that we
+ // generally won't trigger nagle's algorithm in the tcp
+ // implementation: we concatenate if we'd otherwise send
+ // less than PMTU bytes (we assume PMTU is 1460 bytes),
+ // and concatenate up to the largest payload TCP/IP can
+ // carry. with these precautions, nagle's algorithm
+ // should apply only when really appropriate.
+ QByteArray out( 65536 );
+ int j = d->windex;
+ int s = a->size() - j;
+ while ( a && i+s < (int)out.size() ) {
+ memcpy( out.data()+i, a->data()+j, s );
+ j = 0;
+ i += s;
+ a = d->wba.next();
+ s = a ? a->size() : 0;
+ }
+ nwritten = d->socket->write( out.data(), i );
+ if ( d->wsn )
+ d->wsn->setEnabled( false ); // the QSocketNotifier documentation says so
+ } else {
+ // Big block, write it immediately
+ i = a->size() - d->windex;
+ nwritten = d->socket->write( a->data() + d->windex, i );
+ if ( d->wsn )
+ d->wsn->setEnabled( false ); // the QSocketNotifier documentation says so
+ }
+ if ( nwritten > 0 ) {
+ if ( consumeWriteBuf( nwritten ) )
+ consumed += nwritten;
+ }
+ if ( nwritten < i )
+ osBufferFull = true;
+ }
+ if ( consumed > 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): flush: wrote %d bytes, %d left",
+ name(), consumed, (int)d->wsize );
+#endif
+ emit bytesWritten( consumed );
+ }
+ if ( d->state == Closing && d->wsize == 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): flush: Delayed close done. Terminating.",
+ name() );
+#endif
+ resetStatus();
+ setOpenMode(NotOpen);
+ d->close();
+ d->state = Idle;
+ emit delayedCloseFinished();
+ return true;
+ }
+ if ( !d->socket->isOpen() ) {
+ d->connectionClosed();
+ emit connectionClosed();
+ return true;
+ }
+ if ( d->wsn )
+ d->wsn->setEnabled( d->wsize > 0 ); // write if there's data
+ return true;
+}
+
+
+/*!
+ Returns the number of incoming bytes that can be read right now
+ (like bytesAvailable()).
+*/
+
+QIODevice::Offset Q3Socket::size() const
+{
+ return (Offset)bytesAvailable();
+}
+
+
+/*!
+ Returns the current read index. Since Q3Socket is a sequential
+ device, the current read index is always zero.
+*/
+
+QIODevice::Offset Q3Socket::at() const
+{
+ return 0;
+}
+
+
+/*!
+ \overload
+
+ Moves the read index forward to \a index and returns true if the
+ operation was successful; otherwise returns false. Moving the
+ index forward means skipping incoming data.
+*/
+
+bool Q3Socket::at( Offset index )
+{
+ if ( index > d->rba.size() )
+ return false;
+ d->rba.consumeBytes( (Q_ULONG)index, 0 ); // throw away data 0..index-1
+ // After we read data from our internal buffer, if we use the
+ // setReadBufferSize() to limit our buffer, we might now be able to
+ // read more data in our buffer. So enable the read socket notifier,
+ // but do this only if we are not in a slot connected to the
+ // readyRead() signal since this might cause a bad recursive behavior.
+ // We can test for this condition by looking at the
+ // sn_read_alreadyCalled flag.
+ if ( d->rsn && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) == -1 )
+ d->rsn->setEnabled( true );
+ return true;
+}
+
+
+/*!
+ Returns true if there is no more data to read; otherwise returns false.
+*/
+
+bool Q3Socket::atEnd() const
+{
+ if ( d->socket == 0 )
+ return true;
+ Q3Socket * that = (Q3Socket *)this;
+ if ( that->d->socket->bytesAvailable() ) // a little slow, perhaps...
+ that->sn_read();
+ return that->d->rba.size() == 0;
+}
+
+
+/*!
+ Returns the number of incoming bytes that can be read, i.e. the
+ size of the input buffer. Equivalent to size().
+
+ \sa bytesToWrite()
+*/
+
+qint64 Q3Socket::bytesAvailable() const
+{
+ if ( d->socket == 0 )
+ return 0;
+ Q3Socket * that = (Q3Socket *)this;
+ if ( that->d->socket->bytesAvailable() ) // a little slow, perhaps...
+ (void)that->sn_read();
+ return that->d->rba.size() + QIODevice::bytesAvailable();
+}
+
+
+/*!
+ Wait up to \a msecs milliseconds for more data to be available.
+
+ If \a msecs is -1 the call will block indefinitely.
+
+ Returns the number of bytes available.
+
+ If \a timeout is non-null and no error occurred (i.e. it does not
+ return -1): this function sets *\a timeout to true, if the reason
+ for returning was that the timeout was reached; otherwise it sets
+ *\a timeout to false. This is useful to find out if the peer
+ closed the connection.
+
+ \warning This is a blocking call and should be avoided in event
+ driven applications.
+
+ \sa bytesAvailable()
+*/
+
+Q_ULONG Q3Socket::waitForMore( int msecs, bool *timeout ) const
+{
+ if ( d->socket == 0 )
+ return 0;
+ Q3Socket * that = (Q3Socket *)this;
+ if ( that->d->socket->waitForMore( msecs, timeout ) > 0 )
+ (void)that->sn_read( true );
+ return that->d->rba.size();
+}
+
+/*! \overload
+*/
+
+Q_ULONG Q3Socket::waitForMore( int msecs ) const
+{
+ return waitForMore( msecs, 0 );
+}
+
+/*!
+ Returns the number of bytes that are waiting to be written, i.e.
+ the size of the output buffer.
+
+ \sa bytesAvailable() clearPendingData()
+*/
+
+qint64 Q3Socket::bytesToWrite() const
+{
+ return d->wsize;
+}
+
+/*!
+ Deletes the data that is waiting to be written. This is useful if you want
+ to close the socket without waiting for all the data to be written.
+
+ \sa bytesToWrite() close() delayedCloseFinished()
+*/
+
+void Q3Socket::clearPendingData()
+{
+ d->wba.clear();
+ d->windex = d->wsize = 0;
+}
+
+/*!
+ Reads \a maxlen bytes from the socket into \a data and returns the
+ number of bytes read. Returns -1 if an error occurred.
+*/
+
+qint64 Q3Socket::readData( char *data, qint64 maxlen )
+{
+ if ( data == 0 && maxlen != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3Socket::readBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "Q3Socket::readBlock: Socket is not open" );
+#endif
+ return -1;
+ }
+ if ( maxlen >= d->rba.size() )
+ maxlen = d->rba.size();
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): readBlock %d bytes", name(), (int)maxlen );
+#endif
+ d->rba.consumeBytes( maxlen, data );
+ // After we read data from our internal buffer, if we use the
+ // setReadBufferSize() to limit our buffer, we might now be able to
+ // read more data in our buffer. So enable the read socket notifier,
+ // but do this only if we are not in a slot connected to the
+ // readyRead() signal since this might cause a bad recursive behavior.
+ // We can test for this condition by looking at the
+ // sn_read_alreadyCalled flag.
+ if ( d->rsn && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) == -1 )
+ d->rsn->setEnabled( true );
+ return maxlen;
+}
+
+
+/*!
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+*/
+
+qint64 Q3Socket::writeData( const char *data, qint64 len )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && len != 0 ) {
+ qWarning( "Q3Socket::writeBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "Q3Socket::writeBlock: Socket is not open" );
+ return -1;
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( d->state == Closing ) {
+ qWarning( "Q3Socket::writeBlock: Cannot write, socket is closing" );
+ }
+#endif
+ if ( len == 0 || d->state == Closing || d->state == Idle )
+ return 0;
+ QByteArray *a = d->wba.last();
+
+ // next bit is sensitive. if we're writing really small chunks,
+ // try to buffer up since system calls are expensive, and nagle's
+ // algorithm is even more expensive. but if anything even
+ // remotely large is being written, try to issue a write at once.
+
+ bool writeNow = ( d->wsize + len >= 1400 || len > 512 );
+
+ if ( a && a->size() + len < 128 ) {
+ // small buffer, resize
+ int i = a->size();
+ a->resize( i+len );
+ memcpy( a->data()+i, data, len );
+ } else {
+ // append new buffer
+ a = new QByteArray( len );
+ memcpy( a->data(), data, len );
+ d->wba.append( a );
+ }
+ d->wsize += len;
+ if ( writeNow )
+ flush();
+ else if ( d->wsn )
+ d->wsn->setEnabled( true );
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): writeBlock %d bytes", name(), (int)len );
+#endif
+ return len;
+}
+
+
+/*!
+ Reads a single byte/character from the internal read buffer.
+ Returns the byte/character read, or -1 if there is nothing to be
+ read.
+
+ \sa bytesAvailable(), putch()
+*/
+
+int Q3Socket::getch()
+{
+ if ( isOpen() && d->rba.size() > 0 ) {
+ uchar c;
+ d->rba.consumeBytes( 1, (char*)&c );
+ // After we read data from our internal buffer, if we use the
+ // setReadBufferSize() to limit our buffer, we might now be able to
+ // read more data in our buffer. So enable the read socket notifier,
+ // but do this only if we are not in a slot connected to the
+ // readyRead() signal since this might cause a bad recursive behavior.
+ // We can test for this condition by looking at the
+ // sn_read_alreadyCalled flag.
+ if ( d->rsn && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) == -1 )
+ d->rsn->setEnabled( true );
+ return c;
+ }
+ return -1;
+}
+
+
+/*!
+ Writes the character \a ch to the output buffer.
+
+ Returns \a ch, or -1 if an error occurred.
+
+ \sa getch()
+*/
+
+int Q3Socket::putch( int ch )
+{
+ char buf[2];
+ buf[0] = ch;
+ return writeBlock(buf, 1) == 1 ? ch : -1;
+}
+
+
+/*!
+ This implementation of the virtual function QIODevice::ungetch()
+ prepends the character \a ch to the read buffer so that the next
+ read returns this character as the first character of the output.
+*/
+
+int Q3Socket::ungetch( int ch )
+{
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "Q3Socket::ungetch: Socket not open" );
+ return -1;
+ }
+#endif
+ return d->rba.ungetch( ch );
+}
+
+
+/*!
+ Returns true if it's possible to read an entire line of text from
+ this socket at this time; otherwise returns false.
+
+ Note that if the peer closes the connection unexpectedly, this
+ function returns false. This means that loops such as this won't
+ work:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3socket.cpp 0
+
+ \sa readLine()
+*/
+
+bool Q3Socket::canReadLine() const
+{
+ if ( ((Q3Socket*)this)->d->rba.scanNewline( 0 ) )
+ return true;
+ return ( bytesAvailable() > 0 &&
+ (((Q3Socket*)this)->d->rba.scanNewline( 0 ) || QIODevice::canReadLine()) );
+}
+
+/*!
+ \internal
+ Internal slot for handling socket read notifications.
+
+ This function has can usually only be entered once (i.e. no
+ recursive calls). If the argument \a force is true, the function
+ is executed, but no readyRead() signals are emitted. This
+ behaviour is useful for the waitForMore() function, so that it is
+ possible to call waitForMore() in a slot connected to the
+ readyRead() signal.
+*/
+
+void Q3Socket::sn_read( bool force )
+{
+ Q_LONG maxToRead = 0;
+ if ( d->readBufferSize > 0 ) {
+ maxToRead = d->readBufferSize - d->rba.size();
+ if ( maxToRead <= 0 ) {
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ return;
+ }
+ }
+
+ // Use Q3SocketPrivate::sn_read_alreadyCalled to avoid recursive calls of
+ // sn_read() (and as a result avoid emitting the readyRead() signal in a
+ // slot for readyRead(), if you use bytesAvailable()).
+ if ( !force && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) != -1 )
+ return;
+ Q3SocketPrivate::sn_read_alreadyCalled.append( this );
+
+ char buf[4096];
+ Q_LONG nbytes = d->socket->bytesAvailable();
+ Q_LONG nread;
+ QByteArray *a = 0;
+
+ if ( state() == Connecting ) {
+ if ( nbytes > 0 ) {
+ tryConnection();
+ } else {
+ // nothing to do, nothing to care about
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ }
+ if ( state() == Idle ) {
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+
+ if ( nbytes <= 0 ) { // connection closed?
+ // On Windows this may happen when the connection is still open.
+ // This happens when the system is heavily loaded and we have
+ // read all the data on the socket before a new WSAAsyncSelect
+ // event is processed. A new read operation would then block.
+ // This code is also useful when Q3Socket is used without an
+ // event loop.
+ nread = d->socket->readBlock( buf, maxToRead ? QMIN((Q_LONG)sizeof(buf),maxToRead) : sizeof(buf) );
+ if ( nread == 0 ) { // really closed
+ if ( !d->socket->isOpen() ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_read: Connection closed", name() );
+#endif
+ d->connectionClosed();
+ emit connectionClosed();
+ }
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ } else {
+ if ( nread < 0 ) {
+ if ( d->socket->error() == Q3SocketDevice::NoError ) {
+ // all is fine
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+#if defined(Q3SOCKET_DEBUG)
+ qWarning( "Q3Socket::sn_read (%s): Close error", name() );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ emit error( ErrSocketRead );
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ a = new QByteArray( nread );
+ memcpy( a->data(), buf, nread );
+ }
+
+ } else { // data to be read
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_read: %ld incoming bytes", name(), nbytes );
+#endif
+ if ( nbytes > (int)sizeof(buf) ) {
+ // big
+ a = new QByteArray( nbytes );
+ nread = d->socket->readBlock( a->data(), maxToRead ? QMIN(nbytes,maxToRead) : nbytes );
+ } else {
+ a = 0;
+ nread = d->socket->readBlock( buf, maxToRead ? QMIN((Q_LONG)sizeof(buf),maxToRead) : sizeof(buf) );
+ if ( nread > 0 ) {
+ // ##### could setRawData
+ a = new QByteArray( nread );
+ memcpy( a->data(), buf, nread );
+ }
+ }
+ if ( nread == 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_read: Connection closed", name() );
+#endif
+ // ### we should rather ask the socket device if it is closed
+ d->connectionClosed();
+ emit connectionClosed();
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ delete a;
+ return;
+ } else if ( nread < 0 ) {
+ delete a;
+
+ if ( d->socket->error() == Q3SocketDevice::NoError ) {
+ // all is fine
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+#if defined(QT_CHECK_RANGE)
+ qWarning( "Q3Socket::sn_read: Read error" );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ emit error( ErrSocketRead );
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ if ( nread != (int)a->size() ) { // unexpected
+#if defined(CHECK_RANGE) && !defined(Q_OS_WIN32)
+ qWarning( "Q3Socket::sn_read: Unexpected short read" );
+#endif
+ a->resize( nread );
+ }
+ }
+ d->rba.append( a );
+ if ( !force ) {
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ emit readyRead();
+ if ( d->rsn )
+ d->rsn->setEnabled( true );
+ }
+
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+}
+
+
+/*!
+ \internal
+ Internal slot for handling socket write notifications.
+*/
+
+void Q3Socket::sn_write()
+{
+ if ( d->state == Connecting ) // connection established?
+ tryConnection();
+ flush();
+}
+
+void Q3Socket::emitErrorConnectionRefused()
+{
+ emit error( ErrConnectionRefused );
+}
+
+void Q3Socket::tryConnection()
+{
+ if ( d->socket->connect( d->addr, d->port ) ) {
+ d->state = Connected;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_write: Got connection to %s",
+ name(), peerName().ascii() );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( true );
+ emit connected();
+ } else {
+ d->state = Idle;
+ QTimer::singleShot( 0, this, SLOT(emitErrorConnectionRefused()) );
+ return;
+ }
+}
+
+
+/*!
+ Returns the socket number, or -1 if there is no socket at the moment.
+*/
+
+int Q3Socket::socket() const
+{
+ if ( d->socket == 0 )
+ return -1;
+ return d->socket->socket();
+}
+
+/*!
+ Sets the socket to use \a socket and the state() to \c Connected.
+ The socket must already be connected.
+
+ This allows us to use the Q3Socket class as a wrapper for other
+ socket types (e.g. Unix Domain Sockets).
+*/
+
+void Q3Socket::setSocket( int socket )
+{
+ setSocketIntern( socket );
+ d->state = Connection;
+ d->rsn->setEnabled( true );
+}
+
+
+/*!
+ Sets the socket to \a socket. This is used by both setSocket() and
+ connectToHost() and can also be used on unconnected sockets.
+*/
+
+void Q3Socket::setSocketIntern( int socket )
+{
+ if ( state() != Idle ) {
+ clearPendingData();
+ close();
+ }
+ Q_ULONG oldBufferSize = d ? d->readBufferSize : 0;
+ delete d;
+
+ d = new Q3SocketPrivate;
+ if (oldBufferSize)
+ d->readBufferSize = oldBufferSize;
+ if ( socket >= 0 ) {
+ Q3SocketDevice *sd = new Q3SocketDevice( socket, Q3SocketDevice::Stream );
+ sd->setBlocking( false );
+ sd->setAddressReusable( true );
+ d->setSocketDevice( this, sd );
+ }
+ d->state = Idle;
+
+ // Initialize the IO device flags
+ resetStatus();
+ open( IO_ReadWrite );
+
+ // hm... this is not very nice.
+ d->host.clear();
+ d->port = 0;
+#ifndef QT_NO_DNS
+ delete d->dns4;
+ d->dns4 = 0;
+ delete d->dns6;
+ d->dns6 = 0;
+#endif
+}
+
+
+/*!
+ Returns the host port number of this socket, in native byte order.
+*/
+
+Q_UINT16 Q3Socket::port() const
+{
+ if ( d->socket == 0 )
+ return 0;
+ return d->socket->port();
+}
+
+
+/*!
+ Returns the peer's host port number, normally as specified to the
+ connectToHost() function. If none has been set, this function
+ returns 0.
+
+ Note that Qt always uses native byte order, i.e. 67 is 67 in Qt;
+ there is no need to call htons().
+*/
+
+Q_UINT16 Q3Socket::peerPort() const
+{
+ if ( d->socket == 0 )
+ return 0;
+ return d->socket->peerPort();
+}
+
+
+/*!
+ Returns the host address of this socket. (This is normally the
+ main IP address of the host, but can be e.g. 127.0.0.1 for
+ connections to localhost.)
+*/
+
+QHostAddress Q3Socket::address() const
+{
+ if ( d->socket == 0 ) {
+ QHostAddress tmp;
+ return tmp;
+ }
+ return d->socket->address();
+}
+
+
+/*!
+ Returns the address of the connected peer if the socket is in
+ Connected state; otherwise an empty QHostAddress is returned.
+*/
+
+QHostAddress Q3Socket::peerAddress() const
+{
+ if ( d->socket == 0 ) {
+ QHostAddress tmp;
+ return tmp;
+ }
+ return d->socket->peerAddress();
+}
+
+
+/*!
+ Returns the host name as specified to the connectToHost()
+ function. An empty string is returned if none has been set.
+*/
+
+QString Q3Socket::peerName() const
+{
+ return d->host;
+}
+
+/*!
+ Sets the size of the Q3Socket's internal read buffer to \a bufSize.
+
+ Usually Q3Socket reads all data that is available from the operating
+ system's socket. If the buffer size is limited to a certain size, this
+ means that the Q3Socket class doesn't buffer more than this size of data.
+
+ If the size of the read buffer is 0, the read buffer is unlimited and all
+ incoming data is buffered. This is the default.
+
+ If you read the data in the readyRead() signal, you shouldn't use this
+ option since it might slow down your program unnecessary. This option is
+ useful if you only need to read the data at certain points in time, like in
+ a realtime streaming application.
+
+ \sa readBufferSize()
+*/
+
+void Q3Socket::setReadBufferSize( Q_ULONG bufSize )
+{
+ d->readBufferSize = bufSize;
+}
+
+/*!
+ Returns the size of the read buffer.
+
+ \sa setReadBufferSize()
+*/
+
+Q_ULONG Q3Socket::readBufferSize() const
+{
+ return d->readBufferSize;
+}
+
+/*!
+ \fn bool Q3Socket::isSequential() const
+ \internal
+*/
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3socket.h b/src/qt3support/network/q3socket.h
new file mode 100644
index 0000000..c39b572
--- /dev/null
+++ b/src/qt3support/network/q3socket.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SOCKET_H
+#define Q3SOCKET_H
+
+#include <QtCore/qiodevice.h>
+#include <QtNetwork/qhostaddress.h> // int->QHostAddress conversion
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+class Q3SocketPrivate;
+class Q3SocketDevice;
+
+class Q_COMPAT_EXPORT Q3Socket : public QIODevice
+{
+ Q_OBJECT
+public:
+ enum Error {
+ ErrConnectionRefused,
+ ErrHostNotFound,
+ ErrSocketRead
+ };
+
+ Q3Socket( QObject *parent=0, const char *name=0 );
+ virtual ~Q3Socket();
+
+ enum State { Idle, HostLookup, Connecting,
+ Connected, Closing,
+ Connection=Connected };
+ State state() const;
+
+ int socket() const;
+ virtual void setSocket( int );
+
+ Q3SocketDevice *socketDevice();
+ virtual void setSocketDevice( Q3SocketDevice * );
+
+#ifndef QT_NO_DNS
+ virtual void connectToHost( const QString &host, Q_UINT16 port );
+#endif
+ QString peerName() const;
+
+ // Implementation of QIODevice abstract virtual functions
+ bool open( OpenMode mode );
+ bool open(int mode) { return open((OpenMode)mode); }
+ void close();
+ bool flush();
+ Offset size() const;
+ Offset at() const;
+ bool at( Offset );
+ bool atEnd() const;
+
+ qint64 bytesAvailable() const;
+ Q_ULONG waitForMore( int msecs, bool *timeout ) const;
+ Q_ULONG waitForMore( int msecs ) const; // ### Qt 4.0: merge the two overloads
+ qint64 bytesToWrite() const;
+ void clearPendingData();
+
+ int getch();
+ int putch( int );
+ int ungetch(int);
+
+ bool canReadLine() const;
+
+ Q_UINT16 port() const;
+ Q_UINT16 peerPort() const;
+ QHostAddress address() const;
+ QHostAddress peerAddress() const;
+
+ void setReadBufferSize( Q_ULONG );
+ Q_ULONG readBufferSize() const;
+
+ inline bool isSequential() const { return true; }
+
+Q_SIGNALS:
+ void hostFound();
+ void connected();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void bytesWritten( int nbytes );
+ void error( int );
+
+protected Q_SLOTS:
+ virtual void sn_read( bool force=false );
+ virtual void sn_write();
+
+protected:
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+private Q_SLOTS:
+ void tryConnecting();
+ void emitErrorConnectionRefused();
+
+private:
+ Q3SocketPrivate *d;
+
+ bool consumeWriteBuf( Q_ULONG nbytes );
+ void tryConnection();
+ void setSocketIntern( int socket );
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3Socket( const Q3Socket & );
+ Q3Socket &operator=( const Q3Socket & );
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SOCKET_H
diff --git a/src/qt3support/network/q3socketdevice.cpp b/src/qt3support/network/q3socketdevice.cpp
new file mode 100644
index 0000000..0314103
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice.cpp
@@ -0,0 +1,757 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3socketdevice.h"
+#ifndef QT_NO_NETWORK
+
+#include "qwindowdefs.h"
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3SOCKETDEVICE_DEBUG
+
+
+class Q3SocketDevicePrivate
+{
+public:
+ Q3SocketDevicePrivate( Q3SocketDevice::Protocol p )
+ : protocol(p)
+ { }
+
+ Q3SocketDevice::Protocol protocol;
+};
+
+
+/*!
+ \class Q3SocketDevice
+ \brief The Q3SocketDevice class provides a platform-independent low-level socket API.
+
+ \compat
+ \reentrant
+
+ This class provides a low level API for working with sockets. Users of
+ this class are assumed to have networking experience. For most users the
+ Q3Socket class provides a much easier and high level alternative, but
+ certain things (like UDP) can't be done with Q3Socket and if you need a
+ platform-independent API for those, Q3SocketDevice is the right choice.
+
+ The essential purpose of the class is to provide a QIODevice that
+ works on sockets, wrapped in a platform-independent API.
+
+ When calling connect() or bind(), Q3SocketDevice detects the
+ protocol family (IPv4, IPv6) automatically. Passing the protocol
+ family to Q3SocketDevice's constructor or to setSocket() forces
+ creation of a socket device of a specific protocol. If not set, the
+ protocol will be detected at the first call to connect() or bind().
+
+ \sa Q3Socket, QSocketNotifier, QHostAddress
+*/
+
+
+/*!
+ \enum Q3SocketDevice::Protocol
+
+ This enum type describes the protocol family of the socket. Possible values
+ are:
+
+ \value IPv4 The socket is an IPv4 socket.
+ \value IPv6 The socket is an IPv6 socket.
+ \value Unknown The protocol family of the socket is not known. This can
+ happen if you use Q3SocketDevice with an already existing socket; it
+ tries to determine the protocol family, but this can fail if the
+ protocol family is not known to Q3SocketDevice.
+
+ \sa protocol() setSocket()
+*/
+
+/*!
+ \enum Q3SocketDevice::Error
+
+ This enum type describes the error states of Q3SocketDevice.
+
+ \value NoError No error has occurred.
+
+ \value AlreadyBound The device is already bound, according to bind().
+
+ \value Inaccessible The operating system or firewall prohibited
+ the action.
+
+ \value NoResources The operating system ran out of a resource.
+
+ \value InternalError An internal error occurred in Q3SocketDevice.
+
+ \value Impossible An attempt was made to do something which makes
+ no sense. For example:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3socketdevice.cpp 0
+ The libc ::close() closes the socket, but Q3SocketDevice is not aware
+ of this. So when you call writeBlock(), the impossible happens.
+
+ \value NoFiles The operating system will not let Q3SocketDevice open
+ another file.
+
+ \value ConnectionRefused A connection attempt was rejected by the
+ peer.
+
+ \value NetworkFailure There is a network failure.
+
+ \value UnknownError The operating system did something
+ unexpected.
+
+ \omitvalue Bug
+*/
+
+/*!
+ \enum Q3SocketDevice::Type
+
+ This enum type describes the type of the socket:
+ \value Stream a stream socket (TCP, usually)
+ \value Datagram a datagram socket (UDP, usually)
+*/
+
+
+/*!
+ Creates a Q3SocketDevice object for the existing socket \a socket.
+
+ The \a type argument must match the actual socket type; use \c
+ Q3SocketDevice::Stream for a reliable, connection-oriented TCP
+ socket, or Q3SocketDevice::Datagram for an unreliable,
+ connectionless UDP socket.
+*/
+Q3SocketDevice::Q3SocketDevice( int socket, Type type )
+ : fd( socket ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new Q3SocketDevicePrivate(Unknown))
+{
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Created Q3SocketDevice %p (socket %x, type %d)",
+ this, socket, type );
+#endif
+ init();
+ setSocket( socket, type );
+}
+
+/*!
+ Creates a Q3SocketDevice object for a stream or datagram socket.
+
+ The \a type argument must be either Q3SocketDevice::Stream for a
+ reliable, connection-oriented TCP socket, or \c
+ Q3SocketDevice::Datagram for an unreliable UDP socket.
+
+ The socket is created as an IPv4 socket.
+
+ \sa blocking() protocol()
+*/
+Q3SocketDevice::Q3SocketDevice( Type type )
+ : fd( -1 ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new Q3SocketDevicePrivate(IPv4))
+{
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Created Q3SocketDevice object %p, type %d",
+ this, type );
+#endif
+ init();
+ setSocket( createNewSocket(), type );
+}
+
+/*!
+ Creates a Q3SocketDevice object for a stream or datagram socket.
+
+ The \a type argument must be either Q3SocketDevice::Stream for a
+ reliable, connection-oriented TCP socket, or \c
+ Q3SocketDevice::Datagram for an unreliable UDP socket.
+
+ The \a protocol indicates whether the socket should be of type IPv4
+ or IPv6. Passing \c Unknown is not meaningful in this context and you
+ should avoid using (it creates an IPv4 socket, but your code is not easily
+ readable).
+
+ The argument \a dummy is necessary for compatibility with some
+ compilers.
+
+ \sa blocking() protocol()
+*/
+Q3SocketDevice::Q3SocketDevice( Type type, Protocol protocol, int )
+ : fd( -1 ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new Q3SocketDevicePrivate(protocol))
+{
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Created Q3SocketDevice object %p, type %d",
+ this, type );
+#endif
+ init();
+ setSocket( createNewSocket(), type );
+}
+
+/*!
+ Destroys the socket device and closes the socket if it is open.
+*/
+Q3SocketDevice::~Q3SocketDevice()
+{
+ close();
+ delete d;
+ d = 0;
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Destroyed Q3SocketDevice %p", this );
+#endif
+}
+
+
+/*!
+ Returns true if this is a valid socket; otherwise returns false.
+
+ \sa socket()
+*/
+bool Q3SocketDevice::isValid() const
+{
+ return fd != -1;
+}
+
+
+/*!
+ \fn Type Q3SocketDevice::type() const
+
+ Returns the socket type which is either Q3SocketDevice::Stream
+ or Q3SocketDevice::Datagram.
+
+ \sa socket()
+*/
+Q3SocketDevice::Type Q3SocketDevice::type() const
+{
+ return t;
+}
+
+/*!
+ Returns the socket's protocol family, which is one of \c Unknown, \c IPv4,
+ or \c IPv6.
+
+ Q3SocketDevice either creates a socket with a well known protocol family or
+ it uses an already existing socket. In the first case, this function
+ returns the protocol family it was constructed with. In the second case, it
+ tries to determine the protocol family of the socket; if this fails, it
+ returns \c Unknown.
+
+ \sa Protocol setSocket()
+*/
+Q3SocketDevice::Protocol Q3SocketDevice::protocol() const
+{
+ if ( d->protocol == Unknown )
+ d->protocol = getProtocol();
+ return d->protocol;
+}
+
+/*!
+ Returns the socket number, or -1 if it is an invalid socket.
+
+ \sa isValid(), type()
+*/
+int Q3SocketDevice::socket() const
+{
+ return fd;
+}
+
+
+/*!
+ Sets the socket device to operate on the existing socket \a
+ socket.
+
+ The \a type argument must match the actual socket type; use \c
+ Q3SocketDevice::Stream for a reliable, connection-oriented TCP
+ socket, or Q3SocketDevice::Datagram for an unreliable,
+ connectionless UDP socket.
+
+ Any existing socket is closed.
+
+ \sa isValid(), close()
+*/
+void Q3SocketDevice::setSocket( int socket, Type type )
+{
+ if ( fd != -1 ) // close any open socket
+ close();
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::setSocket: socket %x, type %d", socket, type );
+#endif
+ t = type;
+ fd = socket;
+ d->protocol = Unknown;
+ e = NoError;
+ resetStatus();
+ open( ReadWrite );
+ fetchConnectionParameters();
+}
+
+
+/*!
+ Opens the socket using the specified QIODevice file \a mode. This
+ function is called from the Q3SocketDevice constructors and from
+ the setSocket() function. You should not call it yourself.
+
+ \sa close()
+*/
+bool Q3SocketDevice::open( OpenMode mode )
+{
+ if ( isOpen() || !isValid() )
+ return false;
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::open: mode %x", mode );
+#endif
+ setOpenMode( (mode & ReadWrite) | Unbuffered );
+ return true;
+}
+
+/*!
+ \fn bool Q3SocketDevice::open(int mode)
+ \overload
+*/
+/*!
+ The current Q3SocketDevice implementation does not buffer at all,
+ so this is a no-op. This function always returns true.
+*/
+bool Q3SocketDevice::flush()
+{
+ return true;
+}
+
+
+/*!
+ \reimp
+
+ The size is meaningless for a socket, therefore this function returns 0.
+*/
+QIODevice::Offset Q3SocketDevice::size() const
+{
+ return 0;
+}
+
+
+/*!
+ The read/write index is meaningless for a socket, therefore this
+ function returns 0.
+*/
+QIODevice::Offset Q3SocketDevice::at() const
+{
+ return 0;
+}
+
+
+/*!
+ The read/write index is meaningless for a socket, therefore this
+ function does nothing and returns true.
+
+ The \a offset parameter is ignored.
+*/
+bool Q3SocketDevice::at( Offset /* offset */ )
+{
+ return true;
+}
+
+
+/*!
+ \reimp
+
+ Returns true if no data is currently available at the socket;
+ otherwise returns false.
+*/
+bool Q3SocketDevice::atEnd() const
+{
+ return bytesAvailable() <= 0;
+}
+
+/*!
+ Returns true if the address of this socket can be used by other
+ sockets at the same time, and false if this socket claims
+ exclusive ownership.
+
+ \sa setAddressReusable()
+*/
+bool Q3SocketDevice::addressReusable() const
+{
+ return option( ReuseAddress );
+}
+
+
+/*!
+ Sets the address of this socket to be usable by other sockets too
+ if \a enable is true, and to be used exclusively by this socket if
+ \a enable is false.
+
+ When a socket is reusable, other sockets can use the same port
+ number (and IP address), which is generally useful. Of course
+ other sockets cannot use the same
+ (address,port,peer-address,peer-port) 4-tuple as this socket, so
+ there is no risk of confusing the two TCP connections.
+
+ \sa addressReusable()
+*/
+void Q3SocketDevice::setAddressReusable( bool enable )
+{
+ setOption( ReuseAddress, enable );
+}
+
+
+/*!
+ Returns the size of the operating system receive buffer.
+
+ \sa setReceiveBufferSize()
+*/
+int Q3SocketDevice::receiveBufferSize() const
+{
+ return option( ReceiveBuffer );
+}
+
+
+/*!
+ Sets the size of the operating system receive buffer to \a size.
+
+ The operating system receive buffer size effectively limits two
+ things: how much data can be in transit at any one moment, and how
+ much data can be received in one iteration of the main event loop.
+
+ The default is operating system-dependent. A socket that receives
+ large amounts of data is probably best with a buffer size of
+ 49152.
+*/
+void Q3SocketDevice::setReceiveBufferSize( uint size )
+{
+ setOption( ReceiveBuffer, size );
+}
+
+
+/*!
+ Returns the size of the operating system send buffer.
+
+ \sa setSendBufferSize()
+*/
+int Q3SocketDevice::sendBufferSize() const
+{
+ return option( SendBuffer );
+}
+
+
+/*!
+ Sets the size of the operating system send buffer to \a size.
+
+ The operating system send buffer size effectively limits how much
+ data can be in transit at any one moment.
+
+ The default is operating system-dependent. A socket that sends
+ large amounts of data is probably best with a buffer size of
+ 49152.
+*/
+void Q3SocketDevice::setSendBufferSize( uint size )
+{
+ setOption( SendBuffer, size );
+}
+
+
+/*!
+ Returns the port number of this socket device. This may be 0 for a
+ while, but is set to something sensible as soon as a sensible
+ value is available.
+
+ Note that Qt always uses native byte order, i.e. 67 is 67 in Qt;
+ there is no need to call htons().
+*/
+quint16 Q3SocketDevice::port() const
+{
+ return p;
+}
+
+
+/*!
+ Returns the address of this socket device. This may be 0.0.0.0 for
+ a while, but is set to something sensible as soon as a sensible
+ value is available.
+*/
+QHostAddress Q3SocketDevice::address() const
+{
+ return a;
+}
+
+
+/*!
+ Returns the first error seen.
+*/
+Q3SocketDevice::Error Q3SocketDevice::error() const
+{
+ return e;
+}
+
+
+/*!
+ Allows subclasses to set the error state to \a err.
+*/
+void Q3SocketDevice::setError( Error err )
+{
+ e = err;
+}
+
+/*! \fn Q3SocketDevice::readBlock(char *data, Q_ULONG maxlen)
+
+ Reads \a maxlen bytes from the socket into \a data and returns the
+ number of bytes read. Returns -1 if an error occurred. Returning 0
+ is not an error. For Stream sockets, 0 is returned when the remote
+ host closes the connection. For Datagram sockets, 0 is a valid
+ datagram size.
+*/
+
+/*! \fn Q3SocketDevice::writeBlock(const char *data, Q_ULONG len)
+
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+
+ This is used for Q3SocketDevice::Stream sockets.
+*/
+
+/*!
+ \fn Q_LONG Q3SocketDevice::writeBlock( const char * data, Q_ULONG len,
+ const QHostAddress & host, Q_UINT16 port )
+ \overload
+
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+
+ This is used for Q3SocketDevice::Datagram sockets. You must
+ specify the \a host and \a port of the destination of the data.
+*/
+
+/*!
+ \fn bool Q3SocketDevice::isSequential() const
+ \internal
+*/
+
+/*!
+ \fn qint64 Q3SocketDevice::readData( char *data, qint64 maxlen )
+
+ Reads \a maxlen bytes from the socket into \a data and returns the
+ number of bytes read. Returns -1 if an error occurred.
+*/
+
+/*!
+ \fn int Q3SocketDevice::createNewSocket()
+
+ Creates a new socket identifier. Returns -1 if there is a failure
+ to create the new identifier; error() explains why.
+
+ \sa setSocket()
+*/
+
+/*!
+ \fn void Q3SocketDevice::close()
+ \reimp
+
+ Closes the socket and sets the socket identifier to -1 (invalid).
+
+ (This function ignores errors; if there are any then a file
+ descriptor leakage might result. As far as we know, the only error
+ that can arise is EBADF, and that would of course not cause
+ leakage. There may be OS-specific errors that we haven't come
+ across, however.)
+
+ \sa open()
+*/
+
+/*!
+ \fn bool Q3SocketDevice::blocking() const
+
+ Returns true if the socket is valid and in blocking mode;
+ otherwise returns false.
+
+ Note that this function does not set error().
+
+ \warning On Windows, this function always returns true since the
+ ioctlsocket() function is broken.
+
+ \sa setBlocking(), isValid()
+*/
+
+/*!
+ \fn void Q3SocketDevice::setBlocking( bool enable )
+
+ Makes the socket blocking if \a enable is true or nonblocking if
+ \a enable is false.
+
+ Sockets are blocking by default, but we recommend using
+ nonblocking socket operations, especially for GUI programs that
+ need to be responsive.
+
+ \warning On Windows, this function should be used with care since
+ whenever you use a QSocketNotifier on Windows, the socket is
+ immediately made nonblocking.
+
+ \sa blocking(), isValid()
+*/
+
+/*!
+ \fn int Q3SocketDevice::option( Option opt ) const
+
+ Returns the value of the socket option \a opt.
+*/
+
+/*!
+ \fn void Q3SocketDevice::setOption( Option opt, int v )
+
+ Sets the socket option \a opt to \a v.
+*/
+
+/*!
+ \fn bool Q3SocketDevice::connect( const QHostAddress &addr, Q_UINT16 port )
+
+ Connects to the IP address and port specified by \a addr and \a
+ port. Returns true if it establishes a connection; otherwise returns false.
+ If it returns false, error() explains why.
+
+ Note that error() commonly returns NoError for non-blocking
+ sockets; this just means that you can call connect() again in a
+ little while and it'll probably succeed.
+*/
+
+/*!
+ \fn bool Q3SocketDevice::bind( const QHostAddress &address, Q_UINT16 port )
+
+ Assigns a name to an unnamed socket. The name is the host address
+ \a address and the port number \a port. If the operation succeeds,
+ bind() returns true; otherwise it returns false without changing
+ what port() and address() return.
+
+ bind() is used by servers for setting up incoming connections.
+ Call bind() before listen().
+*/
+
+/*!
+ \fn bool Q3SocketDevice::listen( int backlog )
+
+ Specifies how many pending connections a server socket can have.
+ Returns true if the operation was successful; otherwise returns
+ false. A \a backlog value of 50 is quite common.
+
+ The listen() call only applies to sockets where type() is \c
+ Stream, i.e. not to \c Datagram sockets. listen() must not be
+ called before bind() or after accept().
+
+ \sa bind(), accept()
+*/
+
+/*!
+ \fn int Q3SocketDevice::accept()
+
+ Extracts the first connection from the queue of pending
+ connections for this socket and returns a new socket identifier.
+ Returns -1 if the operation failed.
+
+ \sa bind(), listen()
+*/
+
+/*!
+ \fn qint64 Q3SocketDevice::bytesAvailable() const
+
+ Returns the number of bytes available for reading, or -1 if an
+ error occurred.
+
+ \warning On Microsoft Windows, we use the ioctlsocket() function
+ to determine the number of bytes queued on the socket. According
+ to Microsoft (KB Q125486), ioctlsocket() sometimes returns an
+ incorrect number. The only safe way to determine the amount of
+ data on the socket is to read it using readBlock(). QSocket has
+ workarounds to deal with this problem.
+*/
+
+/*!
+ \fn Q_LONG Q3SocketDevice::waitForMore( int msecs, bool *timeout ) const
+
+ Wait up to \a msecs milliseconds for more data to be available. If
+ \a msecs is -1 the call will block indefinitely.
+
+ Returns the number of bytes available for reading, or -1 if an
+ error occurred.
+
+ If \a timeout is non-null and no error occurred (i.e. it does not
+ return -1): this function sets *\a timeout to true, if the reason
+ for returning was that the timeout was reached; otherwise it sets
+ *\a timeout to false. This is useful to find out if the peer
+ closed the connection.
+
+ \warning This is a blocking call and should be avoided in event
+ driven applications.
+
+ \sa bytesAvailable()
+*/
+
+/*!
+ \fn qint64 Q3SocketDevice::writeData( const char *data, qint64 len )
+
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+
+ This is used for Q3SocketDevice::Stream sockets.
+*/
+
+/*!
+ \fn void Q3SocketDevice::fetchConnectionParameters()
+
+ Fetches information about both ends of the connection: whatever is
+ available.
+*/
+
+/*!
+ \fn Q_UINT16 Q3SocketDevice::peerPort() const
+
+ Returns the port number of the port this socket device is
+ connected to. This may be 0 for a while, but is set to something
+ sensible as soon as a sensible value is available.
+
+ Note that for Datagram sockets, this is the source port of the
+ last packet received, and that it is in native byte order.
+*/
+
+/*!
+ \fn QHostAddress Q3SocketDevice::peerAddress() const
+
+ Returns the address of the port this socket device is connected
+ to. This may be 0.0.0.0 for a while, but is set to something
+ sensible as soon as a sensible value is available.
+
+ Note that for Datagram sockets, this is the source port of the
+ last packet received.
+*/
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3socketdevice.h b/src/qt3support/network/q3socketdevice.h
new file mode 100644
index 0000000..8bde1ea
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SOCKETDEVICE_H
+#define Q3SOCKETDEVICE_H
+
+#include <QtCore/qiodevice.h>
+#include <QtNetwork/qhostaddress.h> // int->QHostAddress conversion
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_NETWORK
+
+class Q3SocketDevicePrivate;
+
+class Q_COMPAT_EXPORT Q3SocketDevice: public QIODevice
+{
+public:
+ enum Type { Stream, Datagram };
+ enum Protocol { IPv4, IPv6, Unknown };
+
+ Q3SocketDevice( Type type = Stream );
+ Q3SocketDevice( Type type, Protocol protocol, int dummy );
+ Q3SocketDevice( int socket, Type type );
+ virtual ~Q3SocketDevice();
+
+ bool isValid() const;
+ Type type() const;
+ Protocol protocol() const;
+
+ int socket() const;
+ virtual void setSocket( int socket, Type type );
+
+ bool open( OpenMode mode );
+ bool open( int mode ) { return open((OpenMode)mode); }
+ void close();
+ bool flush();
+
+ // Implementation of QIODevice abstract virtual functions
+ Offset size() const;
+ Offset at() const;
+ bool at( Offset );
+ bool atEnd() const;
+
+ bool blocking() const;
+ virtual void setBlocking( bool );
+
+ bool addressReusable() const;
+ virtual void setAddressReusable( bool );
+
+ int receiveBufferSize() const;
+ virtual void setReceiveBufferSize( uint );
+ int sendBufferSize() const;
+ virtual void setSendBufferSize( uint );
+
+ virtual bool connect( const QHostAddress &, Q_UINT16 );
+
+ virtual bool bind( const QHostAddress &, Q_UINT16 );
+ virtual bool listen( int backlog );
+ virtual int accept();
+
+ qint64 bytesAvailable() const;
+ Q_LONG waitForMore( int msecs, bool *timeout=0 ) const;
+ virtual Q_LONG writeBlock( const char *data, Q_ULONG len,
+ const QHostAddress & host, Q_UINT16 port );
+ inline Q_LONG writeBlock(const char *data, Q_ULONG len)
+ { return qint64(write(data, qint64(len))); }
+ inline qint64 readBlock(char *data, Q_ULONG maxlen)
+ { return qint64(read(data, qint64(maxlen))); }
+
+ Q_UINT16 port() const;
+ Q_UINT16 peerPort() const;
+ QHostAddress address() const;
+ QHostAddress peerAddress() const;
+
+ enum Error {
+ NoError,
+ AlreadyBound,
+ Inaccessible,
+ NoResources,
+ InternalError,
+ Bug = InternalError, // ### remove in 4.0?
+ Impossible,
+ NoFiles,
+ ConnectionRefused,
+ NetworkFailure,
+ UnknownError
+ };
+ Error error() const;
+
+ inline bool isSequential() const { return true; }
+
+protected:
+ void setError( Error err );
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+private:
+ int fd;
+ Type t;
+ Q_UINT16 p;
+ QHostAddress a;
+ Q_UINT16 pp;
+ QHostAddress pa;
+ Q3SocketDevice::Error e;
+ Q3SocketDevicePrivate * d;
+
+ enum Option { Broadcast, ReceiveBuffer, ReuseAddress, SendBuffer };
+
+ int option( Option ) const;
+ virtual void setOption( Option, int );
+
+ void fetchConnectionParameters();
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ void fetchPeerConnectionParameters();
+#endif
+
+ static void init();
+ int createNewSocket();
+ Protocol getProtocol() const;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3SocketDevice( const Q3SocketDevice & );
+ Q3SocketDevice &operator=( const Q3SocketDevice & );
+#endif
+};
+
+#endif // QT_NO_NETWORK
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SOCKETDEVICE_H
diff --git a/src/qt3support/network/q3socketdevice_unix.cpp b/src/qt3support/network/q3socketdevice_unix.cpp
new file mode 100644
index 0000000..afabb19
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice_unix.cpp
@@ -0,0 +1,926 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+// Almost always the same. If not, specify in qplatformdefs.h.
+#if !defined(QT_SOCKOPTLEN_T)
+# define QT_SOCKOPTLEN_T QT_SOCKLEN_T
+#endif
+
+// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED
+static inline int qt_socket_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *addrlen)
+{ return ::accept(s, addr, addrlen); }
+#if defined(accept)
+# undef accept
+#endif
+
+// UnixWare 7 redefines listen -> _listen
+static inline int qt_socket_listen(int s, int backlog)
+{ return ::listen(s, backlog); }
+#if defined(listen)
+# undef listen
+#endif
+
+// UnixWare 7 redefines socket -> _socket
+static inline int qt_socket_socket(int domain, int type, int protocol)
+{ return ::socket(domain, type, protocol); }
+#if defined(socket)
+# undef socket
+#endif
+
+#include "q3socketdevice.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "qwindowdefs.h"
+
+#include <errno.h>
+#include <sys/types.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline void qt_socket_getportaddr( struct sockaddr *sa,
+ Q_UINT16 *port, QHostAddress *addr )
+{
+#if !defined(QT_NO_IPV6)
+ if ( sa->sa_family == AF_INET6 ) {
+ struct sockaddr_in6 *sa6 = ( struct sockaddr_in6 * )sa;
+ Q_IPV6ADDR tmp;
+ memcpy( &tmp, &sa6->sin6_addr.s6_addr, sizeof(tmp) );
+ QHostAddress a( tmp );
+ *addr = a;
+ *port = ntohs( sa6->sin6_port );
+ return;
+ }
+#endif
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+ QHostAddress a( ntohl( sa4->sin_addr.s_addr ) );
+ *port = ntohs( sa4->sin_port );
+ *addr = QHostAddress( ntohl( sa4->sin_addr.s_addr ) );
+ return;
+}
+
+
+//#define QSOCKETDEVICE_DEBUG
+
+// internal
+void Q3SocketDevice::init()
+{
+}
+
+
+Q3SocketDevice::Protocol Q3SocketDevice::getProtocol() const
+{
+ if ( isValid() ) {
+#if !defined (QT_NO_IPV6)
+ struct sockaddr_storage sa;
+#else
+ struct sockaddr sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ QT_SOCKLEN_T sz = sizeof( sa );
+#if !defined (QT_NO_IPV6)
+ struct sockaddr *sap = reinterpret_cast<struct sockaddr *>(&sa);
+ if ( !::getsockname(fd, sap, &sz) ) {
+ switch ( sap->sa_family ) {
+ case AF_INET:
+ return IPv4;
+ case AF_INET6:
+ return IPv6;
+ default:
+ return Unknown;
+ }
+ }
+#else
+ if ( !::getsockname(fd, &sa, &sz) ) {
+ switch ( sa.sa_family ) {
+ case AF_INET:
+ return IPv4;
+ default:
+ return Unknown;
+ }
+ }
+#endif
+ }
+ return Unknown;
+}
+
+
+int Q3SocketDevice::createNewSocket()
+{
+#if !defined(QT_NO_IPV6)
+ int s = qt_socket_socket( protocol() == IPv6 ? AF_INET6 : AF_INET,
+ t == Datagram ? SOCK_DGRAM : SOCK_STREAM, 0 );
+#else
+ int s = qt_socket_socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+#endif
+ if ( s < 0 ) {
+ switch( errno ) {
+ case EPROTONOSUPPORT:
+ e = InternalError; // 0 is supposed to work for both types
+ break;
+ case ENFILE:
+ e = NoFiles; // special case for this
+ break;
+ case EACCES:
+ e = Inaccessible;
+ break;
+ case ENOBUFS:
+ case ENOMEM:
+ e = NoResources;
+ break;
+ case EINVAL:
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ } else {
+ return s;
+ }
+ return -1;
+}
+
+void Q3SocketDevice::close()
+{
+ if ( fd == -1 || !isOpen() ) // already closed
+ return;
+ resetStatus();
+ setOpenMode(NotOpen);
+ ::close( fd );
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::close: Closed socket %x", fd );
+#endif
+ fd = -1;
+ fetchConnectionParameters();
+ QIODevice::close();
+}
+
+
+bool Q3SocketDevice::blocking() const
+{
+ if ( !isValid() )
+ return true;
+ int s = fcntl(fd, F_GETFL, 0);
+ return !(s >= 0 && ((s & O_NDELAY) != 0));
+}
+
+
+void Q3SocketDevice::setBlocking( bool enable )
+{
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::setBlocking( %d )", enable );
+#endif
+ if ( !isValid() )
+ return;
+ int tmp = ::fcntl(fd, F_GETFL, 0);
+ if ( tmp >= 0 )
+ tmp = ::fcntl( fd, F_SETFL, enable ? (tmp&~O_NDELAY) : (tmp|O_NDELAY) );
+ if ( tmp >= 0 )
+ return;
+ if ( e )
+ return;
+ switch( errno ) {
+ case EACCES:
+ case EBADF:
+ e = Impossible;
+ break;
+ case EFAULT:
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ case EDEADLK:
+ case EINTR:
+ case EINVAL:
+ case EMFILE:
+ case ENOLCK:
+ case EPERM:
+ default:
+ e = UnknownError;
+ }
+}
+
+
+int Q3SocketDevice::option( Option opt ) const
+{
+ if ( !isValid() )
+ return -1;
+ int n = -1;
+ int v = -1;
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ }
+ if ( n != -1 ) {
+ QT_SOCKOPTLEN_T len;
+ len = sizeof(v);
+ int r = ::getsockopt( fd, SOL_SOCKET, n, (char*)&v, &len );
+ if ( r >= 0 )
+ return v;
+ if ( !e ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable function
+ switch( errno ) {
+ case EBADF:
+ case ENOTSOCK:
+ that->e = Impossible;
+ break;
+ case EFAULT:
+ that->e = InternalError;
+ break;
+ default:
+ that->e = UnknownError;
+ break;
+ }
+ }
+ return -1;
+ }
+ return v;
+}
+
+
+void Q3SocketDevice::setOption( Option opt, int v )
+{
+ if ( !isValid() )
+ return;
+ int n = -1; // for really, really bad compilers
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ default:
+ return;
+ }
+ if ( ::setsockopt( fd, SOL_SOCKET, n, (char*)&v, sizeof(v)) < 0 &&
+ e == NoError ) {
+ switch( errno ) {
+ case EBADF:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+ case EFAULT:
+ e = InternalError;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+}
+
+
+bool Q3SocketDevice::connect( const QHostAddress &addr, Q_UINT16 port )
+{
+ if ( !isValid() )
+ return false;
+
+ pa = addr;
+ pp = port;
+
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ QT_SOCKLEN_T aalen;
+
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 a6;
+
+ if ( addr.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR ip6 = addr.toIPv6Address();
+ memcpy( &a6.sin6_addr.s6_addr, &ip6, sizeof(ip6) );
+
+ aalen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( addr.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( addr.toIPv4Address() );
+
+ aalen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ int r = QT_SOCKET_CONNECT( fd, aa, aalen );
+ if ( r == 0 ) {
+ fetchConnectionParameters();
+ return true;
+ }
+ if ( errno == EISCONN || errno == EALREADY || errno == EINPROGRESS ) {
+ fetchConnectionParameters();
+ return true;
+ }
+ if ( e != NoError || errno == EAGAIN || errno == EWOULDBLOCK ) {
+ return false;
+ }
+ switch( errno ) {
+ case EBADF:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+ case EFAULT:
+ case EAFNOSUPPORT:
+ e = InternalError;
+ break;
+ case ECONNREFUSED:
+ e = ConnectionRefused;
+ break;
+ case ETIMEDOUT:
+ case ENETUNREACH:
+ e = NetworkFailure;
+ break;
+ case EADDRINUSE:
+ e = NoResources;
+ break;
+ case EACCES:
+ case EPERM:
+ e = Inaccessible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+}
+
+
+bool Q3SocketDevice::bind( const QHostAddress &address, Q_UINT16 port )
+{
+ if ( !isValid() )
+ return false;
+ int r;
+ struct sockaddr_in a4;
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 a6;
+
+ if ( address.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR tmp = address.toIPv6Address();
+ memcpy( &a6.sin6_addr.s6_addr, &tmp, sizeof(tmp) );
+
+ r = QT_SOCKET_BIND( fd, (struct sockaddr *)&a6, sizeof(a6) );
+ } else
+#endif
+ if ( address.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( address.toIPv4Address() );
+
+ r = QT_SOCKET_BIND( fd, (struct sockaddr*)&a4, sizeof(a4) );
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ if ( r < 0 ) {
+ switch( errno ) {
+ case EINVAL:
+ e = AlreadyBound;
+ break;
+ case EACCES:
+ e = Inaccessible;
+ break;
+ case ENOMEM:
+ e = NoResources;
+ break;
+ case EFAULT: // a was illegal
+ case ENAMETOOLONG: // sz was wrong
+ e = InternalError;
+ break;
+ case EBADF: // AF_UNIX only
+ case ENOTSOCK: // AF_UNIX only
+ case EROFS: // AF_UNIX only
+ case ENOENT: // AF_UNIX only
+ case ENOTDIR: // AF_UNIX only
+ case ELOOP: // AF_UNIX only
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+ }
+ fetchConnectionParameters();
+ return true;
+}
+
+
+bool Q3SocketDevice::listen( int backlog )
+{
+ if ( !isValid() )
+ return false;
+ if ( qt_socket_listen( fd, backlog ) >= 0 )
+ return true;
+ if ( !e )
+ e = Impossible;
+ return false;
+}
+
+
+int Q3SocketDevice::accept()
+{
+ if ( !isValid() )
+ return -1;
+
+#if !defined (QT_NO_IPV6)
+ struct sockaddr_storage aa;
+#else
+ struct sockaddr aa;
+#endif
+ QT_SOCKLEN_T l = sizeof( aa );
+ bool done;
+ int s;
+ do {
+ s = qt_socket_accept( fd, (struct sockaddr*)&aa, &l );
+ // we'll blithely throw away the stuff accept() wrote to aa
+ done = true;
+ if ( s < 0 && e == NoError ) {
+ switch( errno ) {
+ case EINTR:
+ done = false;
+ break;
+#if defined(EPROTO)
+ case EPROTO:
+#endif
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case ENOPROTOOPT:
+ case EHOSTDOWN:
+ case EOPNOTSUPP:
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ // in all these cases, an error happened during connection
+ // setup. we're not interested in what happened, so we
+ // just treat it like the client-closed-quickly case.
+ case EPERM:
+ // firewalling wouldn't let us accept. we treat it like
+ // the client-closed-quickly case.
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ // the client closed the connection before we got around
+ // to accept()ing it.
+ break;
+ case EBADF:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+ case EFAULT:
+ e = InternalError;
+ break;
+ case ENOMEM:
+ case ENOBUFS:
+ e = NoResources;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ } while (!done);
+ return s;
+}
+
+
+qint64 Q3SocketDevice::bytesAvailable() const
+{
+ if ( !isValid() )
+ return -1;
+
+ /*
+ Apparently, there is not consistency among different operating
+ systems on how to use FIONREAD.
+
+ FreeBSD, Linux and Solaris all expect the 3rd argument to
+ ioctl() to be an int, which is normally 32-bit even on 64-bit
+ machines.
+
+ IRIX, on the other hand, expects a size_t, which is 64-bit on
+ 64-bit machines.
+
+ So, the solution is to use size_t initialized to zero to make
+ sure all bits are set to zero, preventing underflow with the
+ FreeBSD/Linux/Solaris ioctls.
+ */
+ size_t nbytes = 0;
+ // gives shorter than true amounts on Unix domain sockets.
+ if ( ::ioctl(fd, FIONREAD, (char*)&nbytes) < 0 )
+ return -1;
+ return (Q_LONG) *((int *) &nbytes) + QIODevice::bytesAvailable();
+}
+
+
+Q_LONG Q3SocketDevice::waitForMore( int msecs, bool *timeout ) const
+{
+ if ( !isValid() )
+ return -1;
+ if ( fd >= FD_SETSIZE )
+ return -1;
+
+ fd_set fds;
+ struct timeval tv;
+
+ FD_ZERO( &fds );
+ FD_SET( fd, &fds );
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs % 1000) * 1000;
+
+ int rv = select( fd+1, &fds, 0, 0, msecs < 0 ? 0 : &tv );
+
+ if ( rv < 0 )
+ return -1;
+
+ if ( timeout ) {
+ if ( rv == 0 )
+ *timeout = true;
+ else
+ *timeout = false;
+ }
+
+ return bytesAvailable();
+}
+
+
+qint64 Q3SocketDevice::readData( char *data, qint64 maxlen )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && maxlen != 0 ) {
+ qWarning( "Q3SocketDevice::readBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isValid() ) {
+ qWarning( "Q3SocketDevice::readBlock: Invalid socket" );
+ return -1;
+ }
+ if ( !isOpen() ) {
+ qWarning( "Q3SocketDevice::readBlock: Device is not open" );
+ return -1;
+ }
+ if ( !isReadable() ) {
+ qWarning( "Q3SocketDevice::readBlock: Read operation not permitted" );
+ return -1;
+ }
+#endif
+ bool done = false;
+ int r = 0;
+ while ( done == false ) {
+ if ( t == Datagram ) {
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_storage aa;
+#else
+ struct sockaddr_in aa;
+#endif
+ memset( &aa, 0, sizeof(aa) );
+ QT_SOCKLEN_T sz;
+ sz = sizeof( aa );
+ r = ::recvfrom( fd, data, maxlen, 0,
+ (struct sockaddr *)&aa, &sz );
+
+ qt_socket_getportaddr( (struct sockaddr *)&aa, &pp, &pa);
+
+ } else {
+ r = ::read( fd, data, maxlen );
+ }
+ done = true;
+ if ( r == 0 && t == Stream && maxlen > 0 ) {
+ // connection closed
+ close();
+ } else if ( r >= 0 || errno == EAGAIN || errno == EWOULDBLOCK ) {
+ // nothing
+ } else if ( errno == EINTR ) {
+ done = false;
+ } else if ( e == NoError ) {
+ switch( errno ) {
+ case EIO:
+ case EISDIR:
+ case EBADF:
+ case EINVAL:
+ case EFAULT:
+ case ENOTCONN:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ case EPIPE:
+ case ECONNRESET:
+ // connection closed
+ close();
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+qint64 Q3SocketDevice::writeData( const char *data, qint64 len )
+{
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ bool done = false;
+ int r = 0;
+ bool timeout;
+ while ( !done ) {
+ r = ::write( fd, data, len );
+ done = true;
+ if ( r < 0 && e == NoError &&
+ errno != EAGAIN && errno != EWOULDBLOCK ) {
+ switch( errno ) {
+ case EINTR: // signal - call read() or whatever again
+ done = false;
+ break;
+ case EPIPE:
+ case ECONNRESET:
+ // connection closed
+ close();
+ r = 0;
+ break;
+ case ENOSPC:
+ case EIO:
+ case EISDIR:
+ case EBADF:
+ case EINVAL:
+ case EFAULT:
+ case ENOTCONN:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ } else if ( waitForMore( 0, &timeout ) == 0 ) {
+ if ( !timeout ) {
+ // connection closed
+ close();
+ }
+ }
+ }
+ return r;
+}
+
+
+Q_LONG Q3SocketDevice::writeBlock( const char * data, Q_ULONG len,
+ const QHostAddress & host, Q_UINT16 port )
+{
+ if ( t != Datagram ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Not datagram" );
+#endif
+ return -1; // for now - later we can do t/tcp
+ }
+
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ QT_SOCKLEN_T slen;
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 a6;
+ if ( host.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+
+ Q_IPV6ADDR tmp = host.toIPv6Address();
+ memcpy( &a6.sin6_addr.s6_addr, &tmp, sizeof(tmp) );
+ slen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( host.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( host.toIPv4Address() );
+ slen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return -1;
+ }
+
+ // we'd use MSG_DONTWAIT + MSG_NOSIGNAL if Stevens were right.
+ // but apparently Stevens and most implementors disagree
+ bool done = false;
+ int r = 0;
+ while ( !done ) {
+ r = ::sendto( fd, data, len, 0, aa, slen);
+ done = true;
+ if ( r < 0 && e == NoError &&
+ errno != EAGAIN && errno != EWOULDBLOCK ) {
+ switch( errno ) {
+ case EINTR: // signal - call read() or whatever again
+ done = false;
+ break;
+ case ENOSPC:
+ case EPIPE:
+ case EIO:
+ case EISDIR:
+ case EBADF:
+ case EINVAL:
+ case EFAULT:
+ case ENOTCONN:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+void Q3SocketDevice::fetchConnectionParameters()
+{
+ if ( !isValid() ) {
+ p = 0;
+ a = QHostAddress();
+ pp = 0;
+ pa = QHostAddress();
+ return;
+ }
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ QT_SOCKLEN_T sz;
+ sz = sizeof( sa );
+ if ( !::getsockname( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)&sa, &p, &a );
+
+ sz = sizeof( sa );
+ if ( !::getpeername( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)&sa, &pp, &pa );
+}
+
+
+Q_UINT16 Q3SocketDevice::peerPort() const
+{
+ return pp;
+}
+
+
+QHostAddress Q3SocketDevice::peerAddress() const
+{
+ return pa;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3socketdevice_win.cpp b/src/qt3support/network/q3socketdevice_win.cpp
new file mode 100644
index 0000000..398f3f0
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice_win.cpp
@@ -0,0 +1,1062 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3socketdevice.h"
+#include "qwindowdefs.h"
+#include "qdatetime.h"
+
+#include <qcoreapplication.h>
+
+#include <string.h>
+
+#if defined (QT_NO_IPV6)
+# include <winsock.h>
+#else
+# if defined (Q_CC_BOR) || defined (Q_CC_GNU)
+# include <winsock2.h>
+# else
+# include <winsock.h>
+# endif
+// Use our own defines and structs which we know are correct
+# define QT_SS_MAXSIZE 128
+# define QT_SS_ALIGNSIZE (sizeof(__int64))
+# define QT_SS_PAD1SIZE (QT_SS_ALIGNSIZE - sizeof (short))
+# define QT_SS_PAD2SIZE (QT_SS_MAXSIZE - (sizeof (short) + QT_SS_PAD1SIZE + QT_SS_ALIGNSIZE))
+
+QT_BEGIN_NAMESPACE
+
+struct qt_sockaddr_storage {
+ short ss_family;
+ char __ss_pad1[QT_SS_PAD1SIZE];
+ __int64 __ss_align;
+ char __ss_pad2[QT_SS_PAD2SIZE];
+};
+
+// sockaddr_in6 size changed between old and new SDK
+// Only the new version is the correct one, so always
+// use this structure.
+struct qt_in6_addr {
+ u_char qt_s6_addr[16];
+};
+typedef struct {
+ short sin6_family; /* AF_INET6 */
+ u_short sin6_port; /* Transport level port number */
+ u_long sin6_flowinfo; /* IPv6 flow information */
+ struct qt_in6_addr sin6_addr; /* IPv6 address */
+ u_long sin6_scope_id; /* set of interfaces for a scope */
+} qt_sockaddr_in6;
+#endif
+
+#ifndef AF_INET6
+#define AF_INET6 23 /* Internetwork Version 6 */
+#endif
+
+#ifndef NO_ERRNO_H
+QT_BEGIN_INCLUDE_NAMESPACE
+# if defined(Q_OS_WINCE)
+# include "qfunctions_wince.h"
+# else
+# include <errno.h>
+# endif
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+
+#if defined(SOCKLEN_T)
+#undef SOCKLEN_T
+#endif
+
+#define SOCKLEN_T int // #### Winsock 1.1
+
+static int initialized = 0x00; // Holds the Winsock version
+
+static void cleanupWinSock() // post-routine
+{
+ WSACleanup();
+ initialized = 0x00;
+}
+
+static inline void qt_socket_getportaddr( struct sockaddr *sa,
+ quint16 *port, QHostAddress *addr )
+{
+#if !defined (QT_NO_IPV6)
+ if (sa->sa_family == AF_INET6) {
+ qt_sockaddr_in6 *sa6 = (qt_sockaddr_in6 *)sa;
+ Q_IPV6ADDR tmp;
+ for ( int i = 0; i < 16; ++i )
+ tmp.c[i] = sa6->sin6_addr.qt_s6_addr[i];
+ QHostAddress a( tmp );
+ *addr = a;
+ *port = ntohs( sa6->sin6_port );
+ return;
+ }
+#endif
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+ QHostAddress a( ntohl( sa4->sin_addr.s_addr ) );
+ *port = ntohs( sa4->sin_port );
+ *addr = a;
+}
+
+void Q3SocketDevice::init()
+{
+#if !defined(QT_NO_IPV6)
+ if ( !initialized ) {
+ WSAData wsadata;
+ // IPv6 requires Winsock v2.0 or better.
+ if ( WSAStartup( MAKEWORD(2,0), &wsadata ) != 0 ) {
+# if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: WinSock v2.0 initialization failed, disabling IPv6 support." );
+# endif
+ } else {
+ qAddPostRoutine( cleanupWinSock );
+ initialized = 0x20;
+ return;
+ }
+ }
+#endif
+
+ if (!initialized) {
+ WSAData wsadata;
+ if ( WSAStartup( MAKEWORD(1,1), &wsadata ) != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3SocketDevice: WinSock initialization failed" );
+#endif
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: WinSock initialization failed" );
+#endif
+ return;
+ }
+ qAddPostRoutine( cleanupWinSock );
+ initialized = 0x11;
+ }
+}
+
+Q3SocketDevice::Protocol Q3SocketDevice::getProtocol() const
+{
+ if ( isValid() ) {
+#if !defined (QT_NO_IPV6)
+ struct qt_sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ SOCKLEN_T sz = sizeof( sa );
+ if ( !::getsockname(fd, (struct sockaddr *)&sa, &sz) ) {
+#if !defined (QT_NO_IPV6)
+ switch ( sa.ss_family ) {
+ case AF_INET:
+ return IPv4;
+ case AF_INET6:
+ return IPv6;
+ default:
+ return Unknown;
+ }
+#else
+ switch ( sa.sin_family ) {
+ case AF_INET:
+ return IPv4;
+ default:
+ return Unknown;
+ }
+#endif
+ }
+ }
+ return Unknown;
+}
+
+int Q3SocketDevice::createNewSocket( )
+{
+#if !defined(QT_NO_IPV6)
+ SOCKET s;
+ // Support IPv6 for Winsock v2.0++
+ if ( initialized >= 0x20 && protocol() == IPv6 ) {
+ s = ::socket( AF_INET6, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+ } else {
+ s = ::socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+ }
+#else
+ SOCKET s = ::socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+#endif
+ if ( s == INVALID_SOCKET ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ // ### what to use here?
+ e = NetworkFailure;
+ //e = Inaccessible;
+ break;
+ case WSAEMFILE:
+ e = NoFiles; // special case for this
+ break;
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEAFNOSUPPORT:
+ case WSAEPROTOTYPE:
+ case WSAEPROTONOSUPPORT:
+ case WSAESOCKTNOSUPPORT:
+ e = InternalError;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ } else {
+ return s;
+ }
+ return -1;
+}
+
+
+void Q3SocketDevice::close()
+{
+ if ( fd == -1 || !isOpen() ) // already closed
+ return;
+ resetStatus();
+ setOpenMode(NotOpen);
+ ::closesocket( fd );
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::close: Closed socket %x", fd );
+#endif
+ fd = -1;
+ fetchConnectionParameters();
+ QIODevice::close();
+}
+
+
+bool Q3SocketDevice::blocking() const
+{
+ return true;
+}
+
+
+void Q3SocketDevice::setBlocking( bool enable )
+{
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::setBlocking( %d )", enable );
+#endif
+ if ( !isValid() )
+ return;
+
+ unsigned long dummy = enable ? 0 : 1;
+ ioctlsocket( fd, FIONBIO, &dummy );
+}
+
+
+int Q3SocketDevice::option( Option opt ) const
+{
+ if ( !isValid() )
+ return -1;
+ int n = -1;
+ int v = -1;
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ }
+ if ( n != -1 ) {
+ SOCKLEN_T len = sizeof(v);
+ int r = ::getsockopt( fd, SOL_SOCKET, n, (char*)&v, &len );
+ if ( r != SOCKET_ERROR )
+ return v;
+ if ( !e ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable function
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ that->e = Impossible;
+ break;
+ case WSAENETDOWN:
+ that->e = NetworkFailure;
+ break;
+ case WSAEFAULT:
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ that->e = InternalError;
+ break;
+ case WSAEINPROGRESS:
+ that->e = NoResources;
+ break;
+ case WSAENOTSOCK:
+ that->e = Impossible;
+ break;
+ default:
+ that->e = UnknownError;
+ break;
+ }
+ }
+ return -1;
+ }
+ return v;
+}
+
+
+void Q3SocketDevice::setOption( Option opt, int v )
+{
+ if ( !isValid() )
+ return;
+ int n = -1; // for really, really bad compilers
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ default:
+ return;
+ }
+ int r = ::setsockopt( fd, SOL_SOCKET, n, (char*)&v, sizeof(v) );
+ if ( r == SOCKET_ERROR && e == NoError ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ e = NetworkFailure;
+ break;
+ case WSAEFAULT:
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ e = InternalError;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ break;
+ case WSAENETRESET:
+ case WSAENOTCONN:
+ e = Impossible; // ### ?
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+}
+
+
+bool Q3SocketDevice::connect( const QHostAddress &addr, quint16 port )
+{
+ if ( !isValid() )
+ return false;
+
+ pa = addr;
+ pp = port;
+
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ SOCKLEN_T aalen;
+
+#if !defined(QT_NO_IPV6)
+ qt_sockaddr_in6 a6;
+
+ if ( initialized >= 0x20 && addr.isIPv6Address() ) {
+ memset(&a6, 0, sizeof(a6));
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR ip6 = addr.toIPv6Address();
+ memcpy( &a6.sin6_addr.qt_s6_addr, &ip6, sizeof(ip6) );
+
+ aalen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( addr.isIPv4Address() ) {
+ memset(&a4, 0, sizeof(a4));
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons(port);
+ a4.sin_addr.s_addr = htonl(addr.toIPv4Address());
+
+ aalen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ int r = ::connect( fd, aa, aalen );
+
+ if ( r == SOCKET_ERROR )
+ {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ e = NetworkFailure;
+ break;
+ case WSAEADDRINUSE:
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEINTR:
+ e = UnknownError; // ### ?
+ break;
+ case WSAEALREADY:
+ // ### ?
+ break;
+ case WSAEADDRNOTAVAIL:
+ e = ConnectionRefused; // ### ?
+ break;
+ case WSAEAFNOSUPPORT:
+ case WSAEFAULT:
+ e = InternalError;
+ break;
+ case WSAEINVAL:
+ break;
+ case WSAECONNREFUSED:
+ e = ConnectionRefused;
+ break;
+ case WSAEISCONN:
+ goto successful;
+ case WSAENETUNREACH:
+ case WSAETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ break;
+ case WSAEACCES:
+ e = Inaccessible;
+ break;
+ case 10107:
+ // Workaround for a problem with the WinSock Proxy Server. See
+ // also support/arc-12/25557 for details on the problem.
+ goto successful;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+ }
+successful:
+ fetchConnectionParameters();
+ return true;
+}
+
+
+bool Q3SocketDevice::bind( const QHostAddress &address, quint16 port )
+{
+ if ( !isValid() )
+ return false;
+ int r;
+ struct sockaddr_in a4;
+#if !defined(QT_NO_IPV6)
+ qt_sockaddr_in6 a6;
+
+ if ( initialized >= 0x20 && address.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR tmp = address.toIPv6Address();
+ memcpy( &a6.sin6_addr.qt_s6_addr, &tmp, sizeof(tmp) );
+
+ r = ::bind( fd, (struct sockaddr *)&a6, sizeof(struct qt_sockaddr_storage) );
+ } else
+#endif
+ if ( address.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( address.toIPv4Address() );
+
+ r = ::bind( fd, (struct sockaddr*)&a4, sizeof(struct sockaddr_in) );
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ if ( r == SOCKET_ERROR ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ e = NetworkFailure;
+ break;
+ case WSAEACCES:
+ e = Inaccessible;
+ break;
+ case WSAEADDRNOTAVAIL:
+ e = Inaccessible;
+ break;
+ case WSAEFAULT:
+ e = InternalError;
+ break;
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEADDRINUSE:
+ case WSAEINVAL:
+ e = AlreadyBound;
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+ }
+ fetchConnectionParameters();
+ return true;
+}
+
+
+bool Q3SocketDevice::listen( int backlog )
+{
+ if ( !isValid() )
+ return false;
+ if ( ::listen( fd, backlog ) >= 0 )
+ return true;
+ if ( !e )
+ e = Impossible;
+ return false;
+}
+
+
+int Q3SocketDevice::accept()
+{
+ if ( !isValid() )
+ return -1;
+#if !defined(QT_NO_IPV6)
+ struct qt_sockaddr_storage a;
+#else
+ struct sockaddr a;
+#endif
+ SOCKLEN_T l = sizeof(a);
+ bool done;
+ SOCKET s;
+ do {
+ s = ::accept( fd, (struct sockaddr*)&a, &l );
+ // we'll blithely throw away the stuff accept() wrote to a
+ done = true;
+ if ( s == INVALID_SOCKET && e == NoError ) {
+ switch( WSAGetLastError() ) {
+ case WSAEINTR:
+ done = false;
+ break;
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ case WSAEOPNOTSUPP:
+ // in all these cases, an error happened during connection
+ // setup. we're not interested in what happened, so we
+ // just treat it like the client-closed-quickly case.
+ break;
+ case WSAEFAULT:
+ e = InternalError;
+ break;
+ case WSAEMFILE:
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEINVAL:
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ } while (!done);
+ return s;
+}
+
+
+qint64 Q3SocketDevice::bytesAvailable() const
+{
+ if ( !isValid() )
+ return -1;
+ u_long nbytes = 0;
+ if ( ::ioctlsocket(fd, FIONREAD, &nbytes) < 0 )
+ return -1;
+
+ // ioctlsocket sometimes reports 1 byte available for datagrams
+ // while the following recvfrom returns -1 and claims connection
+ // was reset (udp is connectionless). so we peek one byte to
+ // catch this case and return 0 bytes available if recvfrom
+ // fails.
+ if (nbytes == 1 && t == Datagram) {
+ char c;
+ if (::recvfrom(fd, &c, sizeof(c), MSG_PEEK, 0, 0) == SOCKET_ERROR)
+ return 0;
+ }
+
+ return nbytes;
+}
+
+
+Q_LONG Q3SocketDevice::waitForMore( int msecs, bool *timeout ) const
+{
+ if ( !isValid() )
+ return -1;
+
+ fd_set fds;
+ memset(&fds, 0, sizeof(fd_set));
+ fds.fd_count = 1;
+ fds.fd_array[0] = fd;
+
+ struct timeval tv;
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs % 1000) * 1000;
+
+ int rv = select( fd+1, &fds, 0, 0, msecs < 0 ? 0 : &tv );
+
+ if ( rv < 0 )
+ return -1;
+
+ if ( timeout ) {
+ if ( rv == 0 )
+ *timeout = true;
+ else
+ *timeout = false;
+ }
+
+ return bytesAvailable();
+}
+
+
+qint64 Q3SocketDevice::readData( char *data, qint64 maxlen )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && maxlen != 0 ) {
+ qWarning( "Q3SocketDevice::readBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isValid() ) {
+ qWarning( "Q3SocketDevice::readBlock: Invalid socket" );
+ return -1;
+ }
+ if ( !isOpen() ) {
+ qWarning( "Q3SocketDevice::readBlock: Device is not open" );
+ return -1;
+ }
+ if ( !isReadable() ) {
+ qWarning( "Q3SocketDevice::readBlock: Read operation not permitted" );
+ return -1;
+ }
+#endif
+ qint64 r = 0;
+ if ( t == Datagram ) {
+#if !defined(QT_NO_IPV6)
+ // With IPv6 support, we must be prepared to receive both IPv4
+ // and IPv6 packets. The generic SOCKADDR_STORAGE (struct
+ // sockaddr_storage on unix) replaces struct sockaddr.
+ struct qt_sockaddr_storage a;
+#else
+ struct sockaddr_in a;
+#endif
+ memset( &a, 0, sizeof(a) );
+ SOCKLEN_T sz;
+ sz = sizeof( a );
+ r = ::recvfrom( fd, data, maxlen, 0, (struct sockaddr *)&a, &sz );
+ qt_socket_getportaddr( (struct sockaddr *)(&a), &pp, &pa );
+ } else {
+ r = ::recv( fd, data, maxlen, 0 );
+ }
+ if ( r == 0 && t == Stream && maxlen > 0 ) {
+ if ( WSAGetLastError() != WSAEWOULDBLOCK ) {
+ // connection closed
+ close();
+ }
+ } else if ( r == SOCKET_ERROR && e == NoError ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAECONNABORTED:
+ close();
+ r = 0;
+ break;
+ case WSAETIMEDOUT:
+ case WSAECONNRESET:
+ /*
+ From msdn doc:
+ On a UDP datagram socket this error would indicate that a previous
+ send operation resulted in an ICMP "Port Unreachable" message.
+
+ So we should not close this socket just because one sendto failed.
+ */
+ if ( t != Datagram )
+ close(); // connection closed
+ r = 0;
+ break;
+ case WSAENETDOWN:
+ case WSAENETRESET:
+ e = NetworkFailure;
+ break;
+ case WSAEFAULT:
+ case WSAENOTCONN:
+ case WSAESHUTDOWN:
+ case WSAEINVAL:
+ e = Impossible;
+ break;
+ case WSAEINTR:
+ // ### ?
+ r = 0;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ case WSAEOPNOTSUPP:
+ e = InternalError; // ### ?
+ break;
+ case WSAEWOULDBLOCK:
+ break;
+ case WSAEMSGSIZE:
+ e = NoResources; // ### ?
+ break;
+ case WSAEISCONN:
+ // ### ?
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ return r;
+}
+
+
+qint64 Q3SocketDevice::writeData( const char *data, qint64 len )
+{
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ bool done = false;
+ qint64 r = 0;
+ while ( !done ) {
+ // Don't write more than 64K (see Knowledge Base Q201213).
+ r = ::send( fd, data, ( len>64*1024 ? 64*1024 : len ), 0 );
+ done = true;
+ if ( r == SOCKET_ERROR && e == NoError ) {//&& errno != WSAEAGAIN ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ case WSAEACCES:
+ case WSAENETRESET:
+ case WSAESHUTDOWN:
+ case WSAEHOSTUNREACH:
+ e = NetworkFailure;
+ break;
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ // connection closed
+ close();
+ r = 0;
+ break;
+ case WSAEINTR:
+ done = false;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ // ### perhaps try it later?
+ break;
+ case WSAEFAULT:
+ case WSAEOPNOTSUPP:
+ e = InternalError;
+ break;
+ case WSAENOBUFS:
+ // ### try later?
+ break;
+ case WSAEMSGSIZE:
+ e = NoResources;
+ break;
+ case WSAENOTCONN:
+ case WSAENOTSOCK:
+ case WSAEINVAL:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+Q_LONG Q3SocketDevice::writeBlock( const char * data, Q_ULONG len,
+ const QHostAddress & host, quint16 port )
+{
+ if ( t != Datagram ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Not datagram" );
+#endif
+ return -1; // for now - later we can do t/tcp
+ }
+
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ SOCKLEN_T slen;
+#if !defined(QT_NO_IPV6)
+ qt_sockaddr_in6 a6;
+ if ( initialized >= 0x20 && host.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+
+ Q_IPV6ADDR tmp = host.toIPv6Address();
+ memcpy( &a6.sin6_addr.qt_s6_addr, &tmp, sizeof(tmp) );
+ slen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( host.isIPv4Address() ) {
+
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( host.toIPv4Address() );
+ slen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return -1;
+ }
+
+ // we'd use MSG_DONTWAIT + MSG_NOSIGNAL if Stevens were right.
+ // but apparently Stevens and most implementors disagree
+ bool done = false;
+ qint64 r = 0;
+ while ( !done ) {
+ r = ::sendto( fd, data, len, 0, aa, slen );
+ done = true;
+ if ( r == SOCKET_ERROR && e == NoError ) {//&& e != EAGAIN ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ case WSAEACCES:
+ case WSAENETRESET:
+ case WSAESHUTDOWN:
+ case WSAEHOSTUNREACH:
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ case WSAEADDRNOTAVAIL:
+ case WSAENETUNREACH:
+ case WSAETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ case WSAEINTR:
+ done = false;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ // ### perhaps try it later?
+ break;
+ case WSAEFAULT:
+ case WSAEOPNOTSUPP:
+ case WSAEAFNOSUPPORT:
+ e = InternalError;
+ break;
+ case WSAENOBUFS:
+ case WSAEMSGSIZE:
+ e = NoResources;
+ break;
+ case WSAENOTCONN:
+ case WSAENOTSOCK:
+ case WSAEINVAL:
+ case WSAEDESTADDRREQ:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+void Q3SocketDevice::fetchConnectionParameters()
+{
+ if ( !isValid() ) {
+ p = 0;
+ a = QHostAddress();
+ pp = 0;
+ pa = QHostAddress();
+ return;
+ }
+#if !defined (QT_NO_IPV6)
+ struct qt_sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ SOCKLEN_T sz;
+ sz = sizeof( sa );
+ if ( !::getsockname( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)(&sa), &p, &a );
+ pp = 0;
+ pa = QHostAddress();
+}
+
+
+void Q3SocketDevice::fetchPeerConnectionParameters()
+{
+ // do the getpeername() lazy on Windows (sales/arc-18/37759 claims that
+ // there will be problems otherwise if you use MS Proxy server)
+#if !defined (QT_NO_IPV6)
+ struct qt_sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ SOCKLEN_T sz;
+ sz = sizeof( sa );
+ if ( !::getpeername( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)(&sa), &pp, &pa );
+}
+
+quint16 Q3SocketDevice::peerPort() const
+{
+ if ( pp==0 && isValid() ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable
+ that->fetchPeerConnectionParameters();
+ }
+ return pp;
+}
+
+
+QHostAddress Q3SocketDevice::peerAddress() const
+{
+ if ( pp==0 && isValid() ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable
+ that->fetchPeerConnectionParameters();
+ }
+ return pa;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/network/q3url.cpp b/src/qt3support/network/q3url.cpp
new file mode 100644
index 0000000..b70d780
--- /dev/null
+++ b/src/qt3support/network/q3url.cpp
@@ -0,0 +1,1318 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3url.h"
+
+#ifndef QT_NO_URL
+
+#include "q3cstring.h"
+#include "qdir.h"
+
+QT_BEGIN_NAMESPACE
+
+// used by q3filedialog.cpp
+bool qt_resolve_symlinks = true;
+
+class Q3UrlPrivate
+{
+public:
+ QString protocol;
+ QString user;
+ QString pass;
+ QString host;
+ QString path, cleanPath;
+ QString refEncoded;
+ QString queryEncoded;
+ bool isValid;
+ int port;
+ bool cleanPathDirty;
+};
+
+/*!
+ Replaces backslashes with slashes and removes multiple occurrences
+ of slashes or backslashes if \c allowMultiple is false.
+*/
+
+static void slashify( QString& s, bool allowMultiple = true )
+{
+ bool justHadSlash = false;
+ for ( int i = 0; i < (int)s.length(); i++ ) {
+ if ( !allowMultiple && justHadSlash &&
+ ( s[ i ] == QLatin1Char('/') || s[ i ] == QLatin1Char('\\') ) ) {
+ s.remove( i, 1 );
+ --i;
+ continue;
+ }
+ if ( s[ i ] == QLatin1Char('\\') )
+ s[ i ] = QLatin1Char('/');
+#if defined (Q_WS_MAC9)
+ if ( s[ i ] == QLatin1Char(':') && (i == (int)s.length()-1 || s[ i + 1 ] != QLatin1Char('/') ) ) //mac colon's go away, unless after a protocol
+ s[ i ] = QLatin1Char('/');
+#endif
+ if ( s[ i ] == QLatin1Char('/') )
+ justHadSlash = true;
+ else
+ justHadSlash = false;
+ }
+}
+
+
+
+/*!
+ \class Q3Url
+ \brief The Q3Url class provides a URL parser and simplifies working with URLs.
+
+ \compat
+
+ The Q3Url class is provided for simple work with URLs. It can
+ parse, decode, encode, etc.
+
+ Q3Url works with the decoded path and encoded query in turn.
+
+ Example:
+
+ <tt>http://example.com:80/cgi-bin/test%20me.pl?cmd=Hello%20you</tt>
+
+ \table
+ \header \i Function \i Returns
+ \row \i \l protocol() \i "http"
+ \row \i \l host() \i "example.com"
+ \row \i \l port() \i 80
+ \row \i \l path() \i "/cgi-bin/test&nbsp;me.pl"
+ \row \i \l fileName() \i "test&nbsp;me.pl"
+ \row \i \l query() \i "cmd=Hello%20you"
+ \endtable
+
+ Example:
+
+ <tt>http://qt.nokia.com/doc/qdockarea.html#lines</tt>
+
+ \table
+ \header \i Function \i Returns
+ \row \i \l protocol() \i "http"
+ \row \i \l host() \i "qt.nokia.com"
+ \row \i \l fileName() \i "doc/qdockarea.html"
+ \row \i \l ref() \i "lines"
+ \endtable
+
+ The individual parts of a URL can be set with setProtocol(),
+ setHost(), setPort(), setPath(), setFileName(), setRef() and
+ setQuery(). A URL could contain, for example, an ftp address which
+ requires a user name and password; these can be set with setUser()
+ and setPassword().
+
+ Because path is always encoded internally you must not use "%00"
+ in the path, although this is okay (but not recommended) for the
+ query.
+
+ Q3Url is normally used like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 0
+
+ You can then access and manipulate the various parts of the URL.
+
+ To make it easy to work with Q3Urls and QStrings, Q3Url implements
+ the necessary cast and assignment operators so you can do
+ following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 1
+
+ Use the static functions, encode() and decode() to encode or
+ decode a URL in a string. (They operate on the string in-place.)
+ The isRelativeUrl() static function returns true if the given
+ string is a relative URL.
+
+ If you want to use a URL to work on a hierarchical structure (e.g.
+ a local or remote filesystem), you might want to use the subclass
+ Q3UrlOperator.
+
+ \sa Q3UrlOperator
+*/
+
+
+/*!
+ Constructs an empty URL that is invalid.
+*/
+
+Q3Url::Q3Url()
+{
+ d = new Q3UrlPrivate;
+ d->isValid = false;
+ d->port = -1;
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Constructs a URL by parsing the string \a url.
+
+ If you pass a string like "/home/qt", the "file" protocol is
+ assumed.
+*/
+
+Q3Url::Q3Url( const QString& url )
+{
+ d = new Q3UrlPrivate;
+ d->protocol = QLatin1String("file");
+ d->port = -1;
+ parse( url );
+}
+
+/*!
+ Copy constructor. Copies the data of \a url.
+*/
+
+Q3Url::Q3Url( const Q3Url& url )
+{
+ d = new Q3UrlPrivate;
+ *d = *url.d;
+}
+
+/*!
+ Returns true if \a url is relative; otherwise returns false.
+*/
+
+bool Q3Url::isRelativeUrl( const QString &url )
+{
+ int colon = url.find( QLatin1Char(':') );
+ int slash = url.find( QLatin1Char('/') );
+
+ return ( slash != 0 && ( colon == -1 || ( slash != -1 && colon > slash ) ) );
+}
+
+/*!
+ Constructs an URL taking \a url as the base (context) and
+ \a relUrl as a relative URL to \a url. If \a relUrl is not relative,
+ \a relUrl is taken as the new URL.
+
+ For example, the path of
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 2
+ will be "/qt/srource/qt-2.1.0.tar.gz".
+
+ On the other hand,
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 3
+ will result in a new URL, "ftp://ftp.qt.nokia.com/usr/local",
+ because "/usr/local" isn't relative.
+
+ Similarly,
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 4
+ will result in a new URL, with "/usr/local" as the path
+ and "file" as the protocol.
+
+ Normally it is expected that the path of \a url points to a
+ directory, even if the path has no slash at the end. But if you
+ want the constructor to handle the last part of the path as a file
+ name if there is no slash at the end, and to let it be replaced by
+ the file name of \a relUrl (if it contains one), set \a checkSlash
+ to true.
+*/
+
+Q3Url::Q3Url( const Q3Url& url, const QString& relUrl, bool checkSlash )
+{
+ d = new Q3UrlPrivate;
+ QString rel = relUrl;
+ slashify( rel );
+
+ Q3Url urlTmp( url );
+ if ( !urlTmp.isValid() ) {
+ urlTmp.reset();
+ }
+ if ( isRelativeUrl( rel ) ) {
+ if ( rel[ 0 ] == QLatin1Char('#') ) {
+ *this = urlTmp;
+ rel.remove( (uint)0, 1 );
+ decode( rel );
+ setRef( rel );
+ } else if ( rel[ 0 ] == QLatin1Char('?') ) {
+ *this = urlTmp;
+ rel.remove( (uint)0, 1 );
+ setQuery( rel );
+ } else {
+ decode( rel );
+ *this = urlTmp;
+ setRef( QString() );
+ if ( checkSlash && d->cleanPath[(int)path().length()-1] != QLatin1Char('/') ) {
+ if ( isRelativeUrl( path() ) )
+ setEncodedPathAndQuery( rel );
+ else
+ setFileName( rel );
+ } else {
+ QString p = urlTmp.path();
+ if ( p.isEmpty() ) {
+ // allow URLs like "file:foo"
+ if ( !d->host.isEmpty() && !d->user.isEmpty() && !d->pass.isEmpty() )
+ p = QLatin1String("/");
+ }
+ if ( !p.isEmpty() && !p.endsWith(QLatin1Char('/')) )
+ p += QLatin1Char('/');
+ p += rel;
+ d->path = p;
+ d->cleanPathDirty = true;
+ }
+ }
+ } else {
+ if ( rel[ 0 ] == QChar( QLatin1Char('/') ) ) {
+ *this = urlTmp;
+ setEncodedPathAndQuery( rel );
+ } else {
+ *this = rel;
+ }
+ }
+}
+
+/*!
+ Destructor.
+*/
+
+Q3Url::~Q3Url()
+{
+ delete d;
+ d = 0;
+}
+
+/*!
+ Returns the protocol of the URL. Typically, "file", "http", "ftp",
+ etc.
+
+ \sa setProtocol()
+*/
+
+QString Q3Url::protocol() const
+{
+ return d->protocol;
+}
+
+/*!
+ Sets the protocol of the URL to \a protocol. Typically, "file",
+ "http", "ftp", etc.
+
+ \sa protocol()
+*/
+
+void Q3Url::setProtocol( const QString& protocol )
+{
+ d->protocol = protocol;
+ if ( hasHost() )
+ d->isValid = true;
+}
+
+/*!
+ Returns the username of the URL.
+
+ \sa setUser() setPassword()
+*/
+
+QString Q3Url::user() const
+{
+ return d->user;
+}
+
+/*!
+ Sets the username of the URL to \a user.
+
+ \sa user() setPassword()
+*/
+
+void Q3Url::setUser( const QString& user )
+{
+ d->user = user;
+}
+
+/*!
+ Returns true if the URL contains a username; otherwise returns
+ false.
+
+ \sa setUser() setPassword()
+*/
+
+bool Q3Url::hasUser() const
+{
+ return !d->user.isEmpty();
+}
+
+/*!
+ Returns the password of the URL.
+
+ \warning Passwords passed in URLs are normally \e insecure; this
+ is due to the mechanism, not because of Qt.
+
+ \sa setPassword() setUser()
+*/
+
+QString Q3Url::password() const
+{
+ return d->pass;
+}
+
+/*!
+ Sets the password of the URL to \a pass.
+
+ \warning Passwords passed in URLs are normally \e insecure; this
+ is due to the mechanism, not because of Qt.
+
+ \sa password() setUser()
+*/
+
+void Q3Url::setPassword( const QString& pass )
+{
+ d->pass = pass;
+}
+
+/*!
+ Returns true if the URL contains a password; otherwise returns
+ false.
+
+ \warning Passwords passed in URLs are normally \e insecure; this
+ is due to the mechanism, not because of Qt.
+
+ \sa setPassword() setUser()
+*/
+
+bool Q3Url::hasPassword() const
+{
+ return !d->pass.isEmpty();
+}
+
+/*!
+ Returns the hostname of the URL.
+
+ \sa setHost() hasHost()
+*/
+
+QString Q3Url::host() const
+{
+ return d->host;
+}
+
+/*!
+ Sets the hostname of the URL to \a host.
+
+ \sa host() hasHost()
+*/
+
+void Q3Url::setHost( const QString& host )
+{
+ d->host = host;
+ if ( !d->protocol.isNull() && d->protocol != QLatin1String("file") )
+ d->isValid = true;
+}
+
+/*!
+ Returns true if the URL contains a hostname; otherwise returns
+ false.
+
+ \sa setHost()
+*/
+
+bool Q3Url::hasHost() const
+{
+ return !d->host.isEmpty();
+}
+
+/*!
+ Returns the port of the URL or -1 if no port has been set.
+
+ \sa setPort()
+*/
+
+int Q3Url::port() const
+{
+ return d->port;
+}
+
+/*!
+ Sets the port of the URL to \a port.
+
+ \sa port()
+*/
+
+void Q3Url::setPort( int port )
+{
+ d->port = port;
+}
+
+/*!
+ Returns true if the URL contains a port; otherwise returns false.
+
+ \sa setPort()
+*/
+
+bool Q3Url::hasPort() const
+{
+ return d->port >= 0;
+}
+
+/*!
+ Sets the path of the URL to \a path.
+
+ \sa path() hasPath()
+*/
+
+void Q3Url::setPath( const QString& path )
+{
+ d->path = path;
+ slashify( d->path );
+ d->cleanPathDirty = true;
+ d->isValid = true;
+}
+
+/*!
+ Returns true if the URL contains a path; otherwise returns false.
+
+ \sa path() setPath()
+*/
+
+bool Q3Url::hasPath() const
+{
+ return !d->path.isEmpty();
+}
+
+/*!
+ Sets the query of the URL to \a txt. \a txt must be encoded.
+
+ \sa query() encode()
+*/
+
+void Q3Url::setQuery( const QString& txt )
+{
+ d->queryEncoded = txt;
+}
+
+/*!
+ Returns the (encoded) query of the URL.
+
+ \sa setQuery() decode()
+*/
+
+QString Q3Url::query() const
+{
+ return d->queryEncoded;
+}
+
+/*!
+ Returns the (encoded) reference of the URL.
+
+ \sa setRef() hasRef() decode()
+*/
+
+QString Q3Url::ref() const
+{
+ return d->refEncoded;
+}
+
+/*!
+ Sets the reference of the URL to \a txt. \a txt must be encoded.
+
+ \sa ref() hasRef() encode()
+*/
+
+void Q3Url::setRef( const QString& txt )
+{
+ d->refEncoded = txt;
+}
+
+/*!
+ Returns true if the URL has a reference; otherwise returns false.
+
+ \sa setRef()
+*/
+
+bool Q3Url::hasRef() const
+{
+ return !d->refEncoded.isEmpty();
+}
+
+/*!
+ Returns true if the URL is valid; otherwise returns false. A URL
+ is invalid if it cannot be parsed, for example.
+*/
+
+bool Q3Url::isValid() const
+{
+ return d->isValid;
+}
+
+/*!
+ Resets all parts of the URL to their default values and
+ invalidates it.
+*/
+
+void Q3Url::reset()
+{
+ d->protocol = QLatin1String("file");
+ d->user = QLatin1String("");
+ d->pass = QLatin1String("");
+ d->host = QLatin1String("");
+ d->path = QLatin1String("");
+ d->queryEncoded = QLatin1String("");
+ d->refEncoded = QLatin1String("");
+ d->isValid = true;
+ d->port = -1;
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Parses the \a url. Returns true on success; otherwise returns false.
+*/
+
+bool Q3Url::parse( const QString& url )
+{
+ QString url_( url );
+ slashify( url_ );
+
+ if ( url_.isEmpty() ) {
+ d->isValid = false;
+ return false;
+ }
+
+ d->cleanPathDirty = true;
+ d->isValid = true;
+ QString oldProtocol = d->protocol;
+ d->protocol.clear();
+
+ const int Init = 0;
+ const int Protocol = 1;
+ const int Separator1= 2; // :
+ const int Separator2= 3; // :/
+ const int Separator3= 4; // :// or more slashes
+ const int User = 5;
+ const int Pass = 6;
+ const int Host = 7;
+ const int Path = 8;
+ const int Ref = 9;
+ const int Query = 10;
+ const int Port = 11;
+ const int Done = 12;
+
+ const int InputAlpha= 1;
+ const int InputDigit= 2;
+ const int InputSlash= 3;
+ const int InputColon= 4;
+ const int InputAt = 5;
+ const int InputHash = 6;
+ const int InputQuery= 7;
+
+ static uchar table[ 12 ][ 8 ] = {
+ /* None InputAlpha InputDigit InputSlash InputColon InputAt InputHash InputQuery */
+ { 0, Protocol, 0, Path, 0, 0, 0, 0, }, // Init
+ { 0, Protocol, Protocol, 0, Separator1, 0, 0, 0, }, // Protocol
+ { 0, Path, Path, Separator2, 0, 0, 0, 0, }, // Separator1
+ { 0, Path, Path, Separator3, 0, 0, 0, 0, }, // Separator2
+ { 0, User, User, Separator3, Pass, Host, 0, 0, }, // Separator3
+ { 0, User, User, User, Pass, Host, User, User, }, // User
+ { 0, Pass, Pass, Pass, Pass, Host, Pass, Pass, }, // Pass
+ { 0, Host, Host, Path, Port, Host, Ref, Query, }, // Host
+ { 0, Path, Path, Path, Path, Path, Ref, Query, }, // Path
+ { 0, Ref, Ref, Ref, Ref, Ref, Ref, Query, }, // Ref
+ { 0, Query, Query, Query, Query, Query, Query, Query, }, // Query
+ { 0, 0, Port, Path, 0, 0, 0, 0, } // Port
+ };
+
+ bool relPath = false;
+
+ relPath = false;
+ bool forceRel = false;
+
+ // If ':' is at pos 1, we have only one letter
+ // before that separator => that's a drive letter!
+ if ( url_.length() >= 2 && url_[1] == QLatin1Char(':') )
+ relPath = forceRel = true;
+
+ int hasNoHost = -1;
+ int cs = url_.find( QLatin1String(":/") );
+ if ( cs != -1 ) // if a protocol is there, find out if there is a host or directly the path after it
+ hasNoHost = url_.find( QLatin1String("///"), cs );
+ table[ 4 ][ 1 ] = User;
+ table[ 4 ][ 2 ] = User;
+ if ( cs == -1 || forceRel ) { // we have a relative file
+ if ( url.find( QLatin1Char(':') ) == -1 || forceRel ) {
+ table[ 0 ][ 1 ] = Path;
+ // Filenames may also begin with a digit
+ table[ 0 ][ 2 ] = Path;
+ } else {
+ table[ 0 ][ 1 ] = Protocol;
+ }
+ relPath = true;
+ } else { // some checking
+ table[ 0 ][ 1 ] = Protocol;
+
+ // find the part between the protocol and the path as the meaning
+ // of that part is dependent on some chars
+ ++cs;
+ while ( url_[ cs ] == QLatin1Char('/') )
+ ++cs;
+ int slash = url_.find( QLatin1Char('/'), cs );
+ if ( slash == -1 )
+ slash = url_.length() - 1;
+ QString tmp = url_.mid( cs, slash - cs + 1 );
+
+ if ( !tmp.isEmpty() ) { // if this part exists
+
+ // look for the @ in this part
+ int at = tmp.find( QLatin1Char('@') );
+ if ( at != -1 )
+ at += cs;
+ // we have no @, which means host[:port], so directly
+ // after the protocol the host starts, or if the protocol
+ // is file or there were more than 2 slashes, it is the
+ // path
+ if ( at == -1 ) {
+ if ( url_.left( 4 ) == QLatin1String("file") || hasNoHost != -1 )
+ table[ 4 ][ 1 ] = Path;
+ else
+ table[ 4 ][ 1 ] = Host;
+ table[ 4 ][ 2 ] = table[ 4 ][ 1 ];
+ }
+ }
+ }
+
+ int state = Init; // parse state
+ int input; // input token
+
+ QChar c = url_[ 0 ];
+ int i = 0;
+ QString port;
+
+ for ( ;; ) {
+ switch ( c.latin1() ) {
+ case '?':
+ input = InputQuery;
+ break;
+ case '#':
+ input = InputHash;
+ break;
+ case '@':
+ input = InputAt;
+ break;
+ case ':':
+ input = InputColon;
+ break;
+ case '/':
+ input = InputSlash;
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '0':
+ input = InputDigit;
+ break;
+ default:
+ input = InputAlpha;
+ }
+
+ state = table[ state ][ input ];
+
+ switch ( state ) {
+ case Protocol:
+ d->protocol += c;
+ break;
+ case User:
+ d->user += c;
+ break;
+ case Pass:
+ d->pass += c;
+ break;
+ case Host:
+ d->host += c;
+ break;
+ case Path:
+ d->path += c;
+ break;
+ case Ref:
+ d->refEncoded += c;
+ break;
+ case Query:
+ d->queryEncoded += c;
+ break;
+ case Port:
+ port += c;
+ break;
+ default:
+ break;
+ }
+
+ ++i;
+ if ( i > (int)url_.length() - 1 || state == Done || state == 0 )
+ break;
+ c = url_[ i ];
+
+ }
+
+ if ( !port.isEmpty() ) {
+ port.remove( (uint)0, 1 );
+ d->port = port.toInt();
+ }
+
+ // error
+ if ( i < (int)url_.length() - 1 ) {
+ d->isValid = false;
+ return false;
+ }
+
+
+ if ( d->protocol.isEmpty() )
+ d->protocol = oldProtocol;
+
+ if ( d->path.isEmpty() )
+ d->path = QLatin1String("/");
+
+ // hack for windows
+ if ( d->path.length() == 2 && d->path[ 1 ] == QLatin1Char(':') )
+ d->path += QLatin1Char('/');
+
+ // #### do some corrections, should be done nicer too
+ if ( !d->pass.isEmpty() ) {
+ if ( d->pass[ 0 ] == QLatin1Char(':') )
+ d->pass.remove( (uint)0, 1 );
+ decode( d->pass );
+ }
+ if ( !d->user.isEmpty() ) {
+ decode( d->user );
+ }
+ if ( !d->path.isEmpty() ) {
+ if ( d->path[ 0 ] == QLatin1Char('@') || d->path[ 0 ] == QLatin1Char(':') )
+ d->path.remove( (uint)0, 1 );
+ if ( d->path[ 0 ] != QLatin1Char('/') && !relPath && d->path[ 1 ] != QLatin1Char(':') )
+ d->path.prepend( QLatin1Char('/') );
+ }
+ if ( !d->refEncoded.isEmpty() && d->refEncoded[ 0 ] == QLatin1Char('#') )
+ d->refEncoded.remove( (uint)0, 1 );
+ if ( !d->queryEncoded.isEmpty() && d->queryEncoded[ 0 ] == QLatin1Char('?') )
+ d->queryEncoded.remove( (uint)0, 1 );
+ if ( !d->host.isEmpty() && d->host[ 0 ] == QLatin1Char('@') )
+ d->host.remove( (uint)0, 1 );
+
+#if defined(Q_OS_WIN32)
+ // hack for windows file://machine/path syntax
+ if ( d->protocol == QLatin1String("file") ) {
+ if ( url.startsWith(QLatin1String("file://")) &&
+ d->path.length() > 1 && d->path[ 1 ] != QLatin1Char(':') )
+ d->path.prepend( QLatin1Char('/') );
+ }
+#endif
+
+ decode( d->path );
+ d->cleanPathDirty = true;
+
+#if 0
+ qDebug( "URL: %s", url.latin1() );
+ qDebug( "protocol: %s", d->protocol.latin1() );
+ qDebug( "user: %s", d->user.latin1() );
+ qDebug( "pass: %s", d->pass.latin1() );
+ qDebug( "host: %s", d->host.latin1() );
+ qDebug( "path: %s", path().latin1() );
+ qDebug( "ref: %s", d->refEncoded.latin1() );
+ qDebug( "query: %s", d->queryEncoded.latin1() );
+ qDebug( "port: %d\n\n----------------------------\n\n", d->port );
+#endif
+
+ return true;
+}
+
+/*!
+ \overload
+
+ Parses \a url and assigns the resulting data to this class.
+
+ If you pass a string like "/home/qt" the "file" protocol will be
+ assumed.
+*/
+
+Q3Url& Q3Url::operator=( const QString& url )
+{
+ reset();
+ parse( url );
+
+ return *this;
+}
+
+/*!
+ Assigns the data of \a url to this class.
+*/
+
+Q3Url& Q3Url::operator=( const Q3Url& url )
+{
+ *d = *url.d;
+ return *this;
+}
+
+/*!
+ Compares this URL with \a url and returns true if they are equal;
+ otherwise returns false.
+*/
+
+bool Q3Url::operator==( const Q3Url& url ) const
+{
+ if ( !isValid() || !url.isValid() )
+ return false;
+
+ if ( d->protocol == url.d->protocol &&
+ d->user == url.d->user &&
+ d->pass == url.d->pass &&
+ d->host == url.d->host &&
+ d->path == url.d->path &&
+ d->queryEncoded == url.d->queryEncoded &&
+ d->refEncoded == url.d->refEncoded &&
+ d->isValid == url.d->isValid &&
+ d->port == url.d->port )
+ return true;
+
+ return false;
+}
+
+/*!
+ \overload
+
+ Compares this URL with \a url. \a url is parsed first. Returns
+ true if \a url is equal to this url; otherwise returns false.
+*/
+
+bool Q3Url::operator==( const QString& url ) const
+{
+ Q3Url u( url );
+ return ( *this == u );
+}
+
+/*!
+ Sets the file name of the URL to \a name. If this URL contains a
+ fileName(), the original file name is replaced by \a name.
+
+ See the documentation of fileName() for a more detailed discussion
+ of what is handled as file name and what is handled as a directory
+ path.
+
+ \sa fileName()
+*/
+
+void Q3Url::setFileName( const QString& name )
+{
+ QString fn( name );
+ slashify( fn );
+
+ while ( fn[ 0 ] == QLatin1Char( '/' ) )
+ fn.remove( (uint)0, 1 );
+
+ QString p;
+ if ( path().isEmpty() ) {
+ p = QLatin1String("/");
+ } else {
+ p = path();
+ int slash = p.findRev( QLatin1Char( '/' ) );
+ if ( slash == -1 ) {
+ p = QLatin1String("/");
+ } else if ( p[ (int)p.length() - 1 ] != QLatin1Char( '/' ) ) {
+ p.truncate( slash + 1 );
+ }
+ }
+
+ p += fn;
+ if ( !d->queryEncoded.isEmpty() )
+ p += QLatin1Char('?') + d->queryEncoded;
+ setEncodedPathAndQuery( p );
+}
+
+/*!
+ Returns the encoded path and query.
+
+ \sa decode()
+*/
+
+QString Q3Url::encodedPathAndQuery()
+{
+ QString p = path();
+ if ( p.isEmpty() )
+ p = QLatin1String("/");
+
+ encode( p );
+
+ if ( !d->queryEncoded.isEmpty() ) {
+ p += QLatin1Char('?');
+ p += d->queryEncoded;
+ }
+
+ return p;
+}
+
+/*!
+ Parses \a pathAndQuery for a path and query and sets those values.
+ The whole string must be encoded.
+
+ \sa encode()
+*/
+
+void Q3Url::setEncodedPathAndQuery( const QString& pathAndQuery )
+{
+ d->cleanPathDirty = true;
+ int pos = pathAndQuery.find( QLatin1Char('?') );
+ if ( pos == -1 ) {
+ d->path = pathAndQuery;
+ d->queryEncoded = QLatin1String("");
+ } else {
+ d->path = pathAndQuery.left( pos );
+ d->queryEncoded = pathAndQuery.mid( pos + 1 );
+ }
+
+ decode( d->path );
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Returns the path of the URL. If \a correct is true, the path is
+ cleaned (deals with too many or too few slashes, cleans things
+ like "/../..", etc). Otherwise path() returns exactly the path
+ that was parsed or set.
+
+ \sa setPath() hasPath()
+*/
+QString Q3Url::path( bool correct ) const
+{
+ if ( !correct )
+ return d->path;
+
+ if ( d->cleanPathDirty ) {
+ bool check = true;
+ if ( QDir::isRelativePath( d->path ) ) {
+ d->cleanPath = d->path;
+ } else if ( isLocalFile() ) {
+#if defined(Q_OS_WIN32)
+ // hack for stuff like \\machine\path and //machine/path on windows
+ if ( ( d->path.startsWith(QLatin1Char('/')) || d->path.startsWith(QLatin1Char('\\')) ) &&
+ d->path.length() > 1 ) {
+ d->cleanPath = d->path;
+ bool share = (d->cleanPath[0] == QLatin1Char('\\') && d->cleanPath[1] == QLatin1Char('\\')) ||
+ (d->cleanPath[0] == QLatin1Char('/') && d->cleanPath[1] == QLatin1Char('/'));
+ slashify( d->cleanPath, false );
+ d->cleanPath = QDir::cleanDirPath( d->cleanPath );
+ if ( share ) {
+ check = false;
+ while (d->cleanPath.at(0) != QLatin1Char('/') || d->cleanPath.at(1) != QLatin1Char('/'))
+ d->cleanPath.prepend(QLatin1Char('/'));
+ }
+ }
+#endif
+ if ( check ) {
+ QFileInfo fi( d->path );
+ if ( !fi.exists() )
+ d->cleanPath = d->path;
+ else if ( fi.isDir() ) {
+ QString canPath = QDir( d->path ).canonicalPath();
+ QString dir;
+ if ( qt_resolve_symlinks && !canPath.isNull() )
+ dir = QDir::cleanDirPath( canPath );
+ else
+ dir = QDir::cleanDirPath( QDir( d->path ).absPath() );
+ dir += QLatin1Char('/');
+ if ( dir == QLatin1String("//") )
+ d->cleanPath = QLatin1String("/");
+ else
+ d->cleanPath = dir;
+ } else {
+ QString p =
+ QDir::cleanDirPath( (qt_resolve_symlinks ?
+ fi.dir().canonicalPath() :
+ fi.dir().absPath()) );
+ d->cleanPath = p + QLatin1Char('/') + fi.fileName();
+ }
+ }
+ } else {
+ d->cleanPath = QDir::cleanDirPath( d->path );
+ if ( d->path.length() > 1 && d->path.endsWith(QLatin1Char('/')) )
+ d->cleanPath += QLatin1Char('/');
+ }
+
+ if ( check )
+ slashify( d->cleanPath, false );
+ d->cleanPathDirty = false;
+ }
+
+ return d->cleanPath;
+}
+
+/*!
+ Returns true if the URL is a local file; otherwise returns false.
+*/
+
+bool Q3Url::isLocalFile() const
+{
+ return d->protocol == QLatin1String("file");
+}
+
+/*!
+ Returns the file name of the URL. If the path of the URL doesn't
+ have a slash at the end, the part between the last slash and the
+ end of the path string is considered to be the file name. If the
+ path has a slash at the end, an empty string is returned here.
+
+ \sa setFileName()
+*/
+
+QString Q3Url::fileName() const
+{
+ if ( d->path.isEmpty() || d->path.endsWith( QLatin1Char('/') )
+#ifdef Q_WS_WIN
+ || d->path.endsWith( QLatin1Char('\\') )
+#endif
+ )
+ return QString();
+
+ return QFileInfo( d->path ).fileName();
+}
+
+/*!
+ Adds the path \a pa to the path of the URL.
+
+ \sa setPath() hasPath()
+*/
+
+void Q3Url::addPath( const QString& pa )
+{
+ if ( pa.isEmpty() )
+ return;
+
+ QString p( pa );
+ slashify( p );
+
+ if ( path().isEmpty() ) {
+ if ( p[ 0 ] != QLatin1Char( '/' ) )
+ d->path = QLatin1Char('/') + p;
+ else
+ d->path = p;
+ } else {
+ if ( p[ 0 ] != QLatin1Char( '/' ) && d->path[ (int)d->path.length() - 1 ] != QLatin1Char('/') )
+ d->path += QLatin1Char('/') + p;
+ else
+ d->path += p;
+ }
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Returns the directory path of the URL. This is the part of the
+ path of the URL without the fileName(). See the documentation of
+ fileName() for a discussion of what is handled as file name and
+ what is handled as directory path.
+
+ \sa setPath() hasPath()
+*/
+
+QString Q3Url::dirPath() const
+{
+ if ( path().isEmpty() )
+ return QString();
+
+ QString s = path();
+ int pos = s.findRev( QLatin1Char('/') );
+ if ( pos == -1 ) {
+ return QString::fromLatin1( "." );
+ } else {
+ if ( pos == 0 )
+ return QString::fromLatin1( "/" );
+ return s.left( pos );
+ }
+}
+
+/*!
+ Encodes the \a url in-place into UTF-8. For example
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 5
+
+ \sa decode()
+*/
+
+void Q3Url::encode( QString& url )
+{
+ if ( url.isEmpty() )
+ return;
+
+ Q3CString curl = url.utf8();
+ int oldlen = curl.length();
+
+ const Q3CString special( "+<>#@\"&%$:,;?={}|^~[]\'`\\ \n\t\r" );
+ QString newUrl;
+ int newlen = 0;
+
+ for ( int i = 0; i < oldlen ;++i ) {
+ uchar inCh = (uchar)curl[ i ];
+
+ if ( inCh >= 128 || special.contains(inCh) ) {
+ newUrl[ newlen++ ] = QLatin1Char( '%' );
+
+ ushort c = inCh / 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newUrl[ newlen++ ] = c;
+
+ c = inCh % 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newUrl[ newlen++ ] = c;
+ } else {
+ newUrl[ newlen++ ] = inCh;
+ }
+ }
+
+ url = newUrl;
+}
+
+static uchar hex_to_int( uchar c )
+{
+ if ( c >= 'A' && c <= 'F' )
+ return c - 'A' + 10;
+ if ( c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if ( c >= '0' && c <= '9')
+ return c - '0';
+ return 0;
+}
+
+/*!
+ Decodes the \a url in-place into UTF-8. For example
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 6
+
+ \sa encode()
+*/
+
+void Q3Url::decode( QString& url )
+{
+ if ( url.isEmpty() )
+ return;
+
+ int newlen = 0;
+ Q3CString curl = url.utf8();
+ int oldlen = curl.length();
+
+ Q3CString newUrl(oldlen);
+
+ int i = 0;
+ while ( i < oldlen ) {
+ uchar c = (uchar)curl[ i++ ];
+ if ( c == '%' && i <= oldlen - 2 ) {
+ c = hex_to_int( (uchar)curl[ i ] ) * 16 + hex_to_int( (uchar)curl[ i + 1 ] );
+ i += 2;
+ }
+ newUrl [ newlen++ ] = c;
+ }
+ newUrl.truncate( newlen );
+
+ url = QString::fromUtf8(newUrl.data());
+}
+
+
+/*!
+ Composes a string version of the URL and returns it. If \a
+ encodedPath is true the path in the returned string is encoded. If
+ \a forcePrependProtocol is true and \a encodedPath looks like a
+ local filename, the "file:/" protocol is also prepended.
+
+ \sa encode() decode()
+*/
+
+QString Q3Url::toString( bool encodedPath, bool forcePrependProtocol ) const
+{
+ QString res, p = path();
+ if ( encodedPath )
+ encode( p );
+
+ if ( isLocalFile() ) {
+ if ( forcePrependProtocol )
+ res = d->protocol + QLatin1Char(':') + p;
+ else
+ res = p;
+ } else if ( d->protocol == QLatin1String("mailto") ) {
+ res = d->protocol + QLatin1Char(':') + p;
+ } else {
+ res = d->protocol + QLatin1String("://");
+ if ( !d->user.isEmpty() || !d->pass.isEmpty() ) {
+ QString tmp;
+ if ( !d->user.isEmpty() ) {
+ tmp = d->user;
+ encode( tmp );
+ res += tmp;
+ }
+ if ( !d->pass.isEmpty() ) {
+ tmp = d->pass;
+ encode( tmp );
+ res += QLatin1Char(':') + tmp;
+ }
+ res += QLatin1Char('@');
+ }
+ res += d->host;
+ if ( d->port != -1 )
+ res += QLatin1Char(':') + QString::number( d->port );
+ if ( !p.isEmpty() ) {
+ if ( !d->host.isEmpty() && p[0]!= QLatin1Char( '/' ) )
+ res += QLatin1Char('/');
+ res += p;
+ }
+ }
+
+ if ( !d->refEncoded.isEmpty() )
+ res += QLatin1Char('#') + d->refEncoded;
+ if ( !d->queryEncoded.isEmpty() )
+ res += QLatin1Char('?') + d->queryEncoded;
+
+ return res;
+}
+
+/*!
+ Composes a string version of the URL and returns it.
+
+ \sa Q3Url::toString()
+*/
+
+Q3Url::operator QString() const
+{
+ return toString();
+}
+
+/*!
+ Changes the directory to one directory up. This function always returns
+ true.
+
+ \sa setPath()
+*/
+
+bool Q3Url::cdUp()
+{
+ d->path += QLatin1String("/..");
+ d->cleanPathDirty = true;
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_URL
diff --git a/src/qt3support/network/q3url.h b/src/qt3support/network/q3url.h
new file mode 100644
index 0000000..901e5ce
--- /dev/null
+++ b/src/qt3support/network/q3url.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3URL_H
+#define Q3URL_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_URL
+
+class Q3UrlPrivate;
+
+class Q_COMPAT_EXPORT Q3Url
+{
+public:
+ Q3Url();
+ Q3Url( const QString& url );
+ Q3Url( const Q3Url& url );
+ Q3Url( const Q3Url& url, const QString& relUrl, bool checkSlash = false );
+ virtual ~Q3Url();
+
+ QString protocol() const;
+ virtual void setProtocol( const QString& protocol );
+
+ QString user() const;
+ virtual void setUser( const QString& user );
+ bool hasUser() const;
+
+ QString password() const;
+ virtual void setPassword( const QString& pass );
+ bool hasPassword() const;
+
+ QString host() const;
+ virtual void setHost( const QString& user );
+ bool hasHost() const;
+
+ int port() const;
+ virtual void setPort( int port );
+ bool hasPort() const;
+
+ QString path( bool correct = true ) const;
+ virtual void setPath( const QString& path );
+ bool hasPath() const;
+
+ virtual void setEncodedPathAndQuery( const QString& enc );
+ QString encodedPathAndQuery();
+
+ virtual void setQuery( const QString& txt );
+ QString query() const;
+
+ QString ref() const;
+ virtual void setRef( const QString& txt );
+ bool hasRef() const;
+
+ bool isValid() const;
+ bool isLocalFile() const;
+
+ virtual void addPath( const QString& path );
+ virtual void setFileName( const QString& txt );
+
+ QString fileName() const;
+ QString dirPath() const;
+
+ Q3Url& operator=( const Q3Url& url );
+ Q3Url& operator=( const QString& url );
+
+ bool operator==( const Q3Url& url ) const;
+ bool operator==( const QString& url ) const;
+
+ static void decode( QString& url );
+ static void encode( QString& url );
+
+ operator QString() const;
+ virtual QString toString( bool encodedPath = false, bool forcePrependProtocol = true ) const;
+
+ virtual bool cdUp();
+
+ static bool isRelativeUrl( const QString &url );
+
+protected:
+ virtual void reset();
+ virtual bool parse( const QString& url );
+
+private:
+ Q3UrlPrivate *d;
+
+};
+
+#endif // QT_NO_URL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3URL_H
diff --git a/src/qt3support/network/q3urloperator.cpp b/src/qt3support/network/q3urloperator.cpp
new file mode 100644
index 0000000..401b7d2
--- /dev/null
+++ b/src/qt3support/network/q3urloperator.cpp
@@ -0,0 +1,1212 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3urloperator.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+#include "qurlinfo.h"
+#include "q3networkprotocol.h"
+#include "qmap.h"
+#include "qdir.h"
+#include "q3ptrdict.h"
+#include "qpointer.h"
+#include "q3valuelist.h"
+
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3URLOPERATOR_DEBUG
+
+class Q3UrlOperatorPrivate
+{
+public:
+ Q3UrlOperatorPrivate()
+ {
+ oldOps.setAutoDelete( false );
+ networkProtocol = 0;
+ nameFilter = QLatin1String("*");
+ currPut = 0;
+ }
+
+ ~Q3UrlOperatorPrivate()
+ {
+ delete networkProtocol;
+ while ( oldOps.first() ) {
+ oldOps.first()->free();
+ oldOps.removeFirst();
+ }
+ }
+
+ QMap<QString, QUrlInfo> entryMap;
+ Q3NetworkProtocol *networkProtocol;
+ QString nameFilter;
+ QDir dir;
+
+ // maps needed for copy/move operations
+ Q3PtrDict<Q3NetworkOperation> getOpPutOpMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpPutProtMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpGetProtMap;
+ Q3PtrDict<Q3NetworkOperation> getOpRemoveOpMap;
+ QPointer<Q3NetworkProtocol> currPut;
+ QStringList waitingCopies;
+ QString waitingCopiesDest;
+ bool waitingCopiesMove;
+ Q3PtrList< Q3NetworkOperation > oldOps;
+};
+
+/*!
+ \class Q3UrlOperator
+
+ \brief The Q3UrlOperator class provides common operations on URLs.
+
+ \compat
+
+ \module network
+
+ This class operates on hierarchical structures (such as
+ filesystems) using URLs. Its API facilitates all the common
+ operations:
+ \table
+ \header \i Operation \i Function
+ \row \i List files \i \l listChildren()
+ \row \i Make a directory \i \l mkdir()
+ \row \i Remove a file \i \l remove()
+ \row \i Rename a file \i \l rename()
+ \row \i Get a file \i \l get()
+ \row \i Put a file \i \l put()
+ \row \i Copy a file \i \l copy()
+ \endtable
+
+ You can obtain additional information about the URL with isDir()
+ and info(). If a directory is to be traversed using
+ listChildren(), a name filter can be set with setNameFilter().
+
+ A Q3UrlOperator can be used like this, for example to download a
+ file (and assuming that the FTP protocol is registered):
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 0
+
+ If you want to be notified about success/failure, progress, etc.,
+ you can connect to Q3UrlOperator's signals, e.g. to start(),
+ newChildren(), createdDirectory(), removed(), data(),
+ dataTransferProgress(), startedNextCopy(),
+ connectionStateChanged(), finished(), etc. A network operation can
+ be stopped with stop().
+
+ The class uses the functionality of registered network protocols
+ to perform these operations. Depending of the protocol of the URL,
+ it uses an appropriate network protocol class for the operations.
+ Each of the operation functions of Q3UrlOperator creates a
+ Q3NetworkOperation object that describes the operation and puts it
+ into the operation queue for the network protocol used. If no
+ suitable protocol could be found (because no implementation of the
+ necessary network protocol is registered), the URL operator emits
+ errors. Not every protocol supports every operation, but error
+ handling deals with this problem.
+
+ To register the available network protocols, use the
+ qInitNetworkProtocols() function. The protocols currently
+ supported are:
+ \list
+ \i \link Q3Ftp FTP\endlink,
+ \i \link Q3Http HTTP\endlink,
+ \i \link Q3LocalFs local file system\endlink.
+ \endlist
+
+ \sa Q3NetworkProtocol, Q3NetworkOperation
+*/
+
+/*!
+ \fn void Q3UrlOperator::newChildren( const Q3ValueList<QUrlInfo> &i, Q3NetworkOperation *op )
+
+ This signal is emitted after listChildren() was called and new
+ children (i.e. files) have been read from a list of files. \a i
+ holds the information about the new files. \a op is a pointer
+ to the operation object which contains all the information about
+ the operation, including the state.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+
+/*!
+ \fn void Q3UrlOperator::finished( Q3NetworkOperation *op )
+
+ This signal is emitted when an operation of some sort finishes,
+ whether with success or failure. \a op is a pointer to the
+ operation object, which contains all the information, including
+ the state, of the operation which has been finished. Check the
+ state and error code of the operation object to see whether or not
+ the operation was successful.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::start( Q3NetworkOperation *op )
+
+ Some operations (such as listChildren()) emit this signal when
+ they start processing the operation. \a op is a pointer to the
+ operation object which contains all the information about the
+ operation, including the state.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::createdDirectory( const QUrlInfo &i, Q3NetworkOperation *op )
+
+ This signal is emitted when mkdir() succeeds and the directory has
+ been created. \a i holds the information about the new directory.
+
+ \a op is a pointer to the operation object, which contains all the
+ information about the operation, including the state.
+ \c op->arg(0) holds the new directory's name.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::removed( Q3NetworkOperation *op )
+
+ This signal is emitted when remove() has been successful and the
+ file has been removed.
+
+ \a op is a pointer to the operation object which contains all the
+ information about the operation, including the state.
+ \c op->arg(0) holds the name of the file that was removed.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::itemChanged( Q3NetworkOperation *op )
+
+ This signal is emitted whenever a file which is a child of the URL
+ has been changed, for example by successfully calling rename().
+ \a op is a pointer to the operation object which contains all the
+ information about the operation, including the state.
+ \c op->arg(0) holds the original file name and \c op->arg(1) holds
+ the new file name (if it was changed).
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::data( const QByteArray &data, Q3NetworkOperation *op )
+
+ This signal is emitted when new \a data has been received after calling
+ get() or put().
+ \a op is a pointer to the operation object which contains all
+ the information about the operation, including the state.
+ \c op->arg(0) holds the name of the file whose data is retrieved
+ and op->rawArg(1) holds the (raw) data.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *op )
+
+ This signal is emitted during data transfer (using put() or
+ get()). \a bytesDone specifies how many bytes of \a bytesTotal have
+ been transferred. More information about the operation is stored in
+ \a op, a pointer to the network operation that is processed.
+ \a bytesTotal may be -1, which means that the total number of bytes
+ is not known.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::startedNextCopy( const Q3PtrList<Q3NetworkOperation> &lst )
+
+ This signal is emitted if copy() starts a new copy operation. \a
+ lst contains all Q3NetworkOperations related to this copy
+ operation.
+
+ \sa copy()
+*/
+
+/*!
+ \fn void Q3UrlOperator::connectionStateChanged( int state, const QString &data )
+
+ This signal is emitted whenever the URL operator's connection
+ state changes. \a state describes the new state, which is a
+ \l{Q3NetworkProtocol::ConnectionState} value.
+
+ \a data is a string that describes the change of the connection.
+ This can be used to display a message to the user.
+*/
+
+/*!
+ Constructs a Q3UrlOperator with an empty (i.e. invalid) URL.
+*/
+
+Q3UrlOperator::Q3UrlOperator()
+ : Q3Url()
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 1" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+}
+
+/*!
+ Constructs a Q3UrlOperator using \a url and parses this string.
+
+ If you pass strings like "/home/qt" the "file" protocol is
+ assumed.
+*/
+
+Q3UrlOperator::Q3UrlOperator( const QString &url )
+ : Q3Url( url )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 2" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+ getNetworkProtocol();
+}
+
+/*!
+ Constructs a copy of \a url.
+*/
+
+Q3UrlOperator::Q3UrlOperator( const Q3UrlOperator& url )
+ : QObject(), Q3Url( url )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 3" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+ *d = *url.d;
+
+ d->networkProtocol = 0;
+ getNetworkProtocol();
+ d->nameFilter = QLatin1String("*");
+ d->currPut = 0;
+}
+
+/*!
+ Constructs a Q3UrlOperator. The URL on which this Q3UrlOperator
+ operates is constructed out of the arguments \a url, \a relUrl and
+ \a checkSlash: see the corresponding Q3Url constructor for an
+ explanation of these arguments.
+*/
+
+Q3UrlOperator::Q3UrlOperator( const Q3UrlOperator& url, const QString& relUrl, bool checkSlash )
+ : Q3Url( url, relUrl, checkSlash )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 4" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+ if ( relUrl == QLatin1String(".") )
+ *d = *url.d;
+
+ d->networkProtocol = 0;
+ getNetworkProtocol();
+ d->currPut = 0;
+}
+
+/*!
+ Destructor.
+*/
+
+Q3UrlOperator::~Q3UrlOperator()
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: dstr" );
+#endif
+ delete d;
+}
+
+/*!
+ This private function is used by the simple operation functions,
+ i.e. listChildren(), mkdir(), remove(), rename(), get() and put(),
+ to really start the operation. \a op is a pointer to the network
+ operation that should be started. Returns \a op on success;
+ otherwise returns 0.
+*/
+const Q3NetworkOperation *Q3UrlOperator::startOperation( Q3NetworkOperation *op )
+{
+ if ( !d->networkProtocol )
+ getNetworkProtocol();
+
+ if ( d->networkProtocol && (d->networkProtocol->supportedOperations()&op->operation()) ) {
+ d->networkProtocol->addOperation( op );
+ if ( op->operation() == Q3NetworkProtocol::OpListChildren )
+ clearEntries();
+ return op;
+ }
+
+ // error
+ QString msg;
+ if ( !d->networkProtocol ) {
+ msg = tr( "The protocol `%1' is not supported" ).arg( protocol() );
+ } else {
+ switch ( op->operation() ) {
+ case Q3NetworkProtocol::OpListChildren:
+ msg = tr( "The protocol `%1' does not support listing directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpMkDir:
+ msg = tr( "The protocol `%1' does not support creating new directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpRemove:
+ msg = tr( "The protocol `%1' does not support removing files or directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpRename:
+ msg = tr( "The protocol `%1' does not support renaming files or directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpGet:
+ msg = tr( "The protocol `%1' does not support getting files" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpPut:
+ msg = tr( "The protocol `%1' does not support putting files" ).arg( protocol() );
+ break;
+ default:
+ // this should never happen
+ break;
+ }
+ }
+ op->setState( Q3NetworkProtocol::StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)Q3NetworkProtocol::ErrUnsupported );
+ emit finished( op );
+ deleteOperation( op );
+ return 0;
+}
+
+/*!
+ Starts listing the children of this URL (e.g. the files in the
+ directory). The start() signal is emitted before the first entry
+ is listed and finished() is emitted after the last one. The
+ newChildren() signal is emitted for each list of new entries. If
+ an error occurs, the signal finished() is emitted, so be sure to
+ check the state of the network operation pointer.
+
+ Because the operation may not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ The path of this Q3UrlOperator must to point to a directory
+ (because the children of this directory will be listed), not to a
+ file.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::listChildren()
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpListChildren, QString(), QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ Tries to create a directory (child) with the name \a dirname. If
+ it is successful, a newChildren() signal with the new child is
+ emitted, and the createdDirectory() signal with the information
+ about the new child is also emitted. The finished() signal (with
+ success or failure) is emitted after the operation has been
+ processed, so check the state of the network operation object to
+ see whether or not the operation was successful.
+
+ Because the operation will not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ The path of this Q3UrlOperator must to point to a directory (not a
+ file) because the new directory will be created in this path.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::mkdir( const QString &dirname )
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpMkDir, dirname, QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ Tries to remove the file (child) \a filename. If it succeeds the
+ removed() signal is emitted. finished() (with success or failure)
+ is also emitted after the operation has been processed, so check
+ the state of the network operation object to see whether or not
+ the operation was successful.
+
+ Because the operation will not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ The path of this Q3UrlOperator must point to a directory; because
+ if \a filename is relative, it will try to remove it in this
+ directory.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::remove( const QString &filename )
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpRemove, filename, QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ Tries to rename the file (child) called \a oldname to \a newname.
+ If it succeeds, the itemChanged() signal is emitted. finished()
+ (with success or failure) is also emitted after the operation has
+ been processed, so check the state of the network operation object
+ to see whether or not the operation was successful.
+
+ Because the operation may not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ This path of this Q3UrlOperator must to point to a directory
+ because \a oldname and \a newname are handled relative to this
+ directory.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::rename( const QString &oldname, const QString &newname )
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpRename, oldname, newname, QString() );
+ return startOperation( res );
+}
+
+/*!
+ Copies the file \a from to \a to. If \a move is true, the file is
+ moved (copied and removed). \a from must point to a file and \a to
+ must point to a directory (into which \a from is copied) unless \a
+ toPath is set to false. If \a toPath is set to false then the \a
+ to variable is assumed to be the absolute file path (destination
+ file path + file name). The copying is done using the get() and
+ put() operations. If you want to be notified about the progress of
+ the operation, connect to the dataTransferProgress() signal. Bear
+ in mind that the get() and put() operations emit this signal
+ through the Q3UrlOperator. The number of transferred bytes and the
+ total bytes that you receive as arguments in this signal do not
+ relate to the whole copy operation; they relate first to the
+ get() and then to the put() operation. Always check what type of
+ operation the signal comes from; this is given in the signal's
+ last argument.
+
+ At the end, finished() (with success or failure) is emitted, so
+ check the state of the network operation object to see whether or
+ not the operation was successful.
+
+ Because a move or copy operation consists of multiple operations
+ (get(), put() and maybe remove()), this function doesn't return a
+ single Q3NetworkOperation, but rather a list of them. They are in
+ the order: get(), put() and (if applicable) remove().
+
+ \sa get(), put()
+*/
+
+Q3PtrList<Q3NetworkOperation> Q3UrlOperator::copy( const QString &from, const QString &to, bool move, bool toPath )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: copy %s %s %d", from.latin1(), to.latin1(), move );
+#endif
+
+ Q3PtrList<Q3NetworkOperation> ops;
+ ops.setAutoDelete( false );
+
+ Q3UrlOperator *uFrom = new Q3UrlOperator( *this, from );
+ Q3UrlOperator *uTo = new Q3UrlOperator( to );
+
+ // prepare some string for later usage
+ QString frm = *uFrom;
+ QString file = uFrom->fileName();
+
+ if (frm == to + file)
+ return ops;
+
+ file.prepend( QLatin1Char('/') );
+
+ // uFrom and uTo are deleted when the Q3NetworkProtocol deletes itself via
+ // autodelete
+ uFrom->getNetworkProtocol();
+ uTo->getNetworkProtocol();
+ Q3NetworkProtocol *gProt = uFrom->d->networkProtocol;
+ Q3NetworkProtocol *pProt = uTo->d->networkProtocol;
+
+ uFrom->setPath( uFrom->dirPath() );
+
+ if ( gProt && (gProt->supportedOperations()&Q3NetworkProtocol::OpGet) &&
+ pProt && (pProt->supportedOperations()&Q3NetworkProtocol::OpPut) ) {
+
+ connect( gProt, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ this, SLOT(copyGotData(QByteArray,Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(continueCopy(Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(connectionStateChanged(int,QString)),
+ this, SIGNAL(connectionStateChanged(int,QString)) );
+
+ connect( pProt, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( pProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( pProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(finishedCopy()) );
+
+ Q3NetworkOperation *opGet = new Q3NetworkOperation( Q3NetworkProtocol::OpGet, frm, QString(), QString() );
+ ops.append( opGet );
+ gProt->addOperation( opGet );
+
+
+ QString toFile = to + file;
+ if (!toPath)
+ toFile = to;
+
+ Q3NetworkOperation *opPut = new Q3NetworkOperation( Q3NetworkProtocol::OpPut, toFile, QString(), QString() );
+ ops.append( opPut );
+
+ d->getOpPutProtMap.insert( (void*)opGet, pProt );
+ d->getOpGetProtMap.insert( (void*)opGet, gProt );
+ d->getOpPutOpMap.insert( (void*)opGet, opPut );
+
+ if ( move && (gProt->supportedOperations()&Q3NetworkProtocol::OpRemove) ) {
+ gProt->setAutoDelete( false );
+
+ Q3NetworkOperation *opRm = new Q3NetworkOperation( Q3NetworkProtocol::OpRemove, frm, QString(), QString() );
+ ops.append( opRm );
+ d->getOpRemoveOpMap.insert( (void*)opGet, opRm );
+ } else {
+ gProt->setAutoDelete( true );
+ }
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: copy operation should start now..." );
+#endif
+ return ops;
+ } else {
+ QString msg;
+ if ( !gProt ) {
+ msg = tr( "The protocol `%1' is not supported" ).arg( uFrom->protocol() );
+ } else if ( gProt->supportedOperations() & Q3NetworkProtocol::OpGet ) {
+ msg = tr( "The protocol `%1' does not support copying or moving files or directories" ).arg( uFrom->protocol() );
+ } else if ( !pProt ) {
+ msg = tr( "The protocol `%1' is not supported" ).arg( uTo->protocol() );
+ } else {
+ msg = tr( "The protocol `%1' does not support copying or moving files or directories" ).arg( uTo->protocol() );
+ }
+ delete uFrom;
+ delete uTo;
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpGet, frm, to, QString() );
+ res->setState( Q3NetworkProtocol::StFailed );
+ res->setProtocolDetail( msg );
+ res->setErrorCode( (int)Q3NetworkProtocol::ErrUnsupported );
+ emit finished( res );
+ deleteOperation( res );
+ }
+
+ return ops;
+}
+
+/*!
+ \overload
+
+ Copies the \a files to the directory \a dest. If \a move is true
+ the files are moved, not copied. \a dest must point to a
+ directory.
+
+ This function calls copy() for each entry in \a files in turn. You
+ don't get a result from this function; each time a new copy
+ begins, startedNextCopy() is emitted, with a list of
+ Q3NetworkOperations that describe the new copy operation.
+*/
+
+void Q3UrlOperator::copy( const QStringList &files, const QString &dest,
+ bool move )
+{
+ d->waitingCopies = files;
+ d->waitingCopiesDest = dest;
+ d->waitingCopiesMove = move;
+
+ finishedCopy();
+}
+
+/*!
+ Returns true if the URL is a directory; otherwise returns false.
+ This may not always work correctly, if the protocol of the URL is
+ something other than file (local filesystem). If you pass a bool
+ pointer as the \a ok argument, *\a ok is set to true if the result
+ of this function is known to be correct, and to false otherwise.
+*/
+
+bool Q3UrlOperator::isDir( bool *ok )
+{
+ if ( ok )
+ *ok = true;
+ if ( isLocalFile() ) {
+ if ( QFileInfo( path() ).isDir() )
+ return true;
+ else
+ return false;
+ }
+
+ if ( d->entryMap.contains( QLatin1String(".") ) ) {
+ return d->entryMap[ QLatin1String(".") ].isDir();
+ }
+ // #### can assume that we are a directory?
+ if ( ok )
+ *ok = false;
+ return true;
+}
+
+/*!
+ Tells the network protocol to get data from \a location or, if
+ it is empty, to get data from the location to which this
+ URL points (see Q3Url::fileName() and Q3Url::encodedPathAndQuery()).
+ What happens then depends on the network protocol. The data()
+ signal is emitted when data comes in. Because it's unlikely that
+ all data will come in at once, it is common for multiple data()
+ signals to be emitted. The dataTransferProgress() signal is
+ emitted while processing the operation. At the end, finished()
+ (with success or failure) is emitted, so check the state of the
+ network operation object to see whether or not the operation was
+ successful.
+
+ If \a location is empty, the path of this Q3UrlOperator
+ should point to a file when you use this operation. If \a location
+ is not empty, it can be a relative URL (a child of the path to
+ which the Q3UrlOperator points) or an absolute URL.
+
+ For example, to get a web page you might do something like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 1
+
+ For most other operations, the path of the Q3UrlOperator must point
+ to a directory. If you want to download a file you could do the
+ following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 2
+
+ This will get the data of ftp://ftp.whatever.org/pub/a_file.txt.
+
+ \e Never do anything like this:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 3
+
+ If \a location is not empty and relative it must not contain any
+ queries or references, just the name of a child. So if you need to
+ specify a query or reference, do it as shown in the first example
+ or specify the full URL (such as
+ http://www.whatever.org/cgi-bin/search.pl?cmd=Hello) as \a location.
+
+ \sa copy()
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::get( const QString &location )
+{
+ Q3Url u( *this );
+ if ( !location.isEmpty() )
+ u = Q3Url( *this, location );
+
+ if ( !u.isValid() )
+ return 0;
+
+ if ( !d->networkProtocol ) {
+ setProtocol( u.protocol() );
+ getNetworkProtocol();
+ }
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpGet, u, QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ This function tells the network protocol to put \a data in \a
+ location. If \a location is empty, it puts the \a data in the
+ location to which the URL points. What happens depends on
+ the network protocol. Depending on the network protocol, some
+ data might come back after putting data, in which case the data()
+ signal is emitted. The dataTransferProgress() signal is emitted
+ during processing of the operation. At the end, finished() (with
+ success or failure) is emitted, so check the state of the network
+ operation object to see whether or not the operation was
+ successful.
+
+ If \a location is empty, the path of this Q3UrlOperator should
+ point to a file when you use this operation. If \a location
+ is not empty, it can be a relative (a child of the path to which
+ the Q3UrlOperator points) or an absolute URL.
+
+ For putting some data to a file you can do the following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 4
+
+ For most other operations, the path of the Q3UrlOperator must point
+ to a directory. If you want to upload data to a file you could do
+ the following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 5
+
+ This will upload the data to ftp://ftp.whatever.com/home/me/filename.dat.
+
+ \sa copy()
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::put( const QByteArray &data, const QString &location )
+{
+ Q3Url u( *this );
+ if ( !location.isEmpty() )
+ u = Q3Url( *this, location );
+
+ if ( !u.isValid() )
+ return 0;
+
+ if ( !d->networkProtocol ) {
+ setProtocol( u.protocol() );
+ getNetworkProtocol();
+ }
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpPut, u, QString(), QString() );
+ res->setRawArg( 1, data );
+ return startOperation( res );
+}
+
+/*!
+ Sets the name filter of the URL to \a nameFilter.
+
+ \sa QDir::setNameFilter()
+*/
+
+void Q3UrlOperator::setNameFilter( const QString &nameFilter )
+{
+ d->nameFilter = nameFilter;
+}
+
+/*!
+ Returns the name filter of the URL.
+
+ \sa Q3UrlOperator::setNameFilter() QDir::nameFilter()
+*/
+
+QString Q3UrlOperator::nameFilter() const
+{
+ return d->nameFilter;
+}
+
+/*!
+ Clears the cache of children.
+*/
+
+void Q3UrlOperator::clearEntries()
+{
+ d->entryMap.clear();
+}
+
+/*!
+ Adds an entry to the cache of children.
+*/
+
+void Q3UrlOperator::addEntry( const Q3ValueList<QUrlInfo> &i )
+{
+ Q3ValueList<QUrlInfo>::ConstIterator it = i.begin();
+ for ( ; it != i.end(); ++it )
+ d->entryMap[ ( *it ).name().stripWhiteSpace() ] = *it;
+}
+
+/*!
+ Returns the URL information for the child \a entry, or returns an
+ empty QUrlInfo object if there is no information available about
+ \a entry. Information about \a entry is only available after a successfully
+ finished listChildren() operation.
+*/
+
+QUrlInfo Q3UrlOperator::info( const QString &entry ) const
+{
+ if ( d->entryMap.contains( entry.stripWhiteSpace() ) ) {
+ return d->entryMap[ entry.stripWhiteSpace() ];
+ } else if ( entry == QLatin1String(".") || entry == QLatin1String("..") ) {
+ // return a faked QUrlInfo
+ QUrlInfo inf;
+ inf.setName( entry );
+ inf.setDir( true );
+ inf.setFile( false );
+ inf.setSymLink( false );
+ inf.setOwner( tr( "(unknown)" ) );
+ inf.setGroup( tr( "(unknown)" ) );
+ inf.setSize( 0 );
+ inf.setWritable( false );
+ inf.setReadable( true );
+ return inf;
+ }
+ return QUrlInfo();
+}
+
+/*!
+ Finds a network protocol for the URL and deletes the old network protocol.
+*/
+
+void Q3UrlOperator::getNetworkProtocol()
+{
+ delete d->networkProtocol;
+ Q3NetworkProtocol *p = Q3NetworkProtocol::getNetworkProtocol( protocol() );
+ if ( !p ) {
+ d->networkProtocol = 0;
+ return;
+ }
+
+ d->networkProtocol = (Q3NetworkProtocol *)p;
+ d->networkProtocol->setUrl( this );
+ connect( d->networkProtocol, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ this, SLOT(slotItemChanged(Q3NetworkOperation*)) );
+}
+
+/*!
+ Deletes the currently used network protocol.
+*/
+
+void Q3UrlOperator::deleteNetworkProtocol()
+{
+ if (d->networkProtocol) {
+ d->networkProtocol->deleteLater();
+ d->networkProtocol = 0;
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3UrlOperator::setPath( const QString& path )
+{
+ Q3Url::setPath( path );
+ if ( d->networkProtocol )
+ d->networkProtocol->setUrl( this );
+}
+
+/*!
+ \reimp
+*/
+
+void Q3UrlOperator::reset()
+{
+ Q3Url::reset();
+ deleteNetworkProtocol();
+ d->nameFilter = QLatin1String("*");
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3UrlOperator::parse( const QString &url )
+{
+ bool b = Q3Url::parse( url );
+ if ( !b ) {
+ return b;
+ }
+
+ getNetworkProtocol();
+
+ return b;
+}
+
+/*!
+ Assigns \a url to this object.
+*/
+
+Q3UrlOperator& Q3UrlOperator::operator=( const Q3UrlOperator &url )
+{
+ deleteNetworkProtocol();
+ Q3Url::operator=( url );
+
+ Q3PtrDict<Q3NetworkOperation> getOpPutOpMap = d->getOpPutOpMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpPutProtMap = d->getOpPutProtMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpGetProtMap = d->getOpGetProtMap;
+ Q3PtrDict<Q3NetworkOperation> getOpRemoveOpMap = d->getOpRemoveOpMap;
+
+ *d = *url.d;
+
+ d->oldOps.setAutoDelete( false );
+ d->getOpPutOpMap = getOpPutOpMap;
+ d->getOpPutProtMap = getOpPutProtMap;
+ d->getOpGetProtMap = getOpGetProtMap;
+ d->getOpRemoveOpMap = getOpRemoveOpMap;
+
+ d->networkProtocol = 0;
+ getNetworkProtocol();
+ return *this;
+}
+
+/*!
+ Assigns \a url to this object.
+*/
+
+Q3UrlOperator& Q3UrlOperator::operator=( const QString &url )
+{
+ deleteNetworkProtocol();
+ Q3Url::operator=( url );
+ d->oldOps.setAutoDelete( false );
+ getNetworkProtocol();
+ return *this;
+}
+
+/*!
+ \internal
+*/
+
+bool Q3UrlOperator::cdUp()
+{
+ bool b = Q3Url::cdUp();
+ if ( d->networkProtocol )
+ d->networkProtocol->setUrl( this );
+ return b;
+}
+
+/*!
+ \internal
+*/
+
+bool Q3UrlOperator::checkValid()
+{
+ // ######
+ if ( !isValid() ) {
+ //emit error( ErrValid, tr( "The entered URL is not valid!" ) );
+ return false;
+ } else
+ return true;
+}
+
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::copyGotData( const QByteArray &data_, Q3NetworkOperation *op )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: copyGotData: %d new bytes", data_.size() );
+#endif
+ Q3NetworkOperation *put = d->getOpPutOpMap[ (void*)op ];
+ if ( put ) {
+ QByteArray &s = put->raw( 1 );
+ int size = s.size();
+ s.resize( size + data_.size() );
+ memcpy( s.data() + size, data_.data(), data_.size() );
+ }
+ emit data( data_, op );
+}
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::continueCopy( Q3NetworkOperation *op )
+{
+ if ( op->operation() != Q3NetworkProtocol::OpGet )
+ return;
+ if ( op->state()!=Q3NetworkProtocol::StDone && op->state()!=Q3NetworkProtocol::StFailed ) {
+ return;
+ }
+
+#ifdef Q3URLOPERATOR_DEBUG
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ qDebug( "Q3UrlOperator: continue copy (get finished, put will start)" );
+ }
+#endif
+
+ Q3NetworkOperation *put = d->getOpPutOpMap[ (void*)op ];
+ Q3NetworkProtocol *gProt = d->getOpGetProtMap[ (void*)op ];
+ Q3NetworkProtocol *pProt = d->getOpPutProtMap[ (void*)op ];
+ Q3NetworkOperation *rm = d->getOpRemoveOpMap[ (void*)op ];
+ d->getOpPutOpMap.take( op );
+ d->getOpGetProtMap.take( op );
+ d->getOpPutProtMap.take( op );
+ d->getOpRemoveOpMap.take( op );
+ if ( pProt )
+ pProt->setAutoDelete( true );
+ if ( put && pProt ) {
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ pProt->addOperation( put );
+ d->currPut = pProt;
+ if (rm) { // we need the result of the put operation
+ qApp->processEvents(); // process posted operations
+ if (put->state() == Q3NetworkProtocol::StFailed) {
+ deleteOperation( rm );
+ rm = 0;
+ }
+ }
+ } else {
+ deleteOperation( put );
+ }
+ }
+ if ( gProt ) {
+ gProt->setAutoDelete( true );
+ }
+ if ( rm && gProt ) {
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ gProt->addOperation( rm );
+ } else {
+ deleteOperation( rm );
+ }
+ }
+ disconnect( gProt, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ this, SLOT(copyGotData(QByteArray,Q3NetworkOperation*)) );
+ disconnect( gProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(continueCopy(Q3NetworkOperation*)) );
+}
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::finishedCopy()
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: finished copy (finished putting)" );
+#endif
+
+ if ( d->waitingCopies.isEmpty() )
+ return;
+
+ QString cp = d->waitingCopies.first();
+ d->waitingCopies.remove( cp );
+ Q3PtrList<Q3NetworkOperation> lst = copy( cp, d->waitingCopiesDest, d->waitingCopiesMove );
+ emit startedNextCopy( lst );
+}
+
+/*!
+ Stops the current network operation and removes all this
+ Q3UrlOperator's waiting network operations.
+*/
+
+void Q3UrlOperator::stop()
+{
+ d->getOpPutOpMap.clear();
+ d->getOpRemoveOpMap.clear();
+ d->getOpGetProtMap.setAutoDelete( true );
+ d->getOpPutProtMap.setAutoDelete( true );
+ Q3PtrDictIterator<Q3NetworkProtocol> it( d->getOpPutProtMap );
+ for ( ; it.current(); ++it )
+ it.current()->stop();
+ d->getOpPutProtMap.clear();
+ it = Q3PtrDictIterator<Q3NetworkProtocol>( d->getOpGetProtMap );
+ for ( ; it.current(); ++it )
+ it.current()->stop();
+ d->getOpGetProtMap.clear();
+ if ( d->currPut ) {
+ d->currPut->stop();
+ delete (Q3NetworkProtocol *) d->currPut;
+ d->currPut = 0;
+ }
+ d->waitingCopies.clear();
+ if ( d->networkProtocol )
+ d->networkProtocol->stop();
+ getNetworkProtocol();
+}
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::deleteOperation( Q3NetworkOperation *op )
+{
+ if ( op )
+ d->oldOps.append( op );
+}
+
+/*!
+ \internal
+ updates the entryMap after a network operation finished
+*/
+
+void Q3UrlOperator::slotItemChanged( Q3NetworkOperation *op )
+{
+ if ( !op )
+ return;
+
+ switch ( op->operation() ) {
+ case Q3NetworkProtocol::OpRename :
+ {
+ if ( op->arg( 0 ) == op->arg( 1 ) )
+ return;
+
+ QMap<QString, QUrlInfo>::iterator mi = d->entryMap.find( op->arg( 0 ) );
+ if ( mi != d->entryMap.end() ) {
+ mi.data().setName( op->arg( 1 ) );
+ d->entryMap[ op->arg( 1 ) ] = mi.data();
+ d->entryMap.erase( mi );
+ }
+ break;
+ }
+ case Q3NetworkProtocol::OpRemove :
+ {
+ QMap<QString, QUrlInfo>::iterator mi = d->entryMap.find( op->arg( 0 ) );
+ if ( mi != d->entryMap.end() )
+ d->entryMap.erase( mi );
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_q3urloperator.cpp"
+
+#endif // QT_NO_NETWORKPROTOCOL
diff --git a/src/qt3support/network/q3urloperator.h b/src/qt3support/network/q3urloperator.h
new file mode 100644
index 0000000..de1ea90
--- /dev/null
+++ b/src/qt3support/network/q3urloperator.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3URLOPERATOR_H
+#define Q3URLOPERATOR_H
+
+#include <QtCore/qobject.h>
+#include <Qt3Support/q3url.h>
+#include <Qt3Support/q3ptrlist.h>
+#include <Qt3Support/q3networkprotocol.h>
+#include <QtCore/qstringlist.h> // QString->QStringList conversion
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+class QUrlInfo;
+class Q3UrlOperatorPrivate;
+class Q3NetworkProtocol;
+
+class Q_COMPAT_EXPORT Q3UrlOperator : public QObject, public Q3Url
+{
+ Q_OBJECT
+ friend class Q3NetworkProtocol;
+
+public:
+ Q3UrlOperator();
+ Q3UrlOperator( const QString &urL );
+ Q3UrlOperator( const Q3UrlOperator& url );
+ Q3UrlOperator( const Q3UrlOperator& url, const QString& relUrl, bool checkSlash = false );
+ virtual ~Q3UrlOperator();
+
+ virtual void setPath( const QString& path );
+ virtual bool cdUp();
+
+ virtual const Q3NetworkOperation *listChildren();
+ virtual const Q3NetworkOperation *mkdir( const QString &dirname );
+ virtual const Q3NetworkOperation *remove( const QString &filename );
+ virtual const Q3NetworkOperation *rename( const QString &oldname, const QString &newname );
+ virtual const Q3NetworkOperation *get( const QString &location = QString() );
+ virtual const Q3NetworkOperation *put( const QByteArray &data, const QString &location = QString() );
+ virtual Q3PtrList<Q3NetworkOperation> copy( const QString &from, const QString &to, bool move = false, bool toPath = true );
+ virtual void copy( const QStringList &files, const QString &dest, bool move = false );
+ virtual bool isDir( bool *ok = 0 );
+
+ virtual void setNameFilter( const QString &nameFilter );
+ QString nameFilter() const;
+
+ virtual QUrlInfo info( const QString &entry ) const;
+
+ Q3UrlOperator& operator=( const Q3UrlOperator &url );
+ Q3UrlOperator& operator=( const QString &url );
+
+ virtual void stop();
+
+Q_SIGNALS:
+ void newChildren( const Q3ValueList<QUrlInfo> &, Q3NetworkOperation *res );
+ void finished( Q3NetworkOperation *res );
+ void start( Q3NetworkOperation *res );
+ void createdDirectory( const QUrlInfo &, Q3NetworkOperation *res );
+ void removed( Q3NetworkOperation *res );
+ void itemChanged( Q3NetworkOperation *res );
+ void data( const QByteArray &, Q3NetworkOperation *res );
+ void dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *res );
+ void startedNextCopy( const Q3PtrList<Q3NetworkOperation> &lst );
+ void connectionStateChanged( int state, const QString &data );
+
+protected:
+ void reset();
+ bool parse( const QString& url );
+ virtual bool checkValid();
+ virtual void clearEntries();
+ void getNetworkProtocol();
+ void deleteNetworkProtocol();
+
+private Q_SLOTS:
+ const Q3NetworkOperation *startOperation( Q3NetworkOperation *op );
+ void copyGotData( const QByteArray &data, Q3NetworkOperation *op );
+ void continueCopy( Q3NetworkOperation *op );
+ void finishedCopy();
+ void addEntry( const Q3ValueList<QUrlInfo> &i );
+ void slotItemChanged( Q3NetworkOperation *op );
+
+private:
+ void deleteOperation( Q3NetworkOperation *op );
+
+ Q3UrlOperatorPrivate *d;
+};
+
+#endif // QT_NO_NETWORKPROTOCOL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3URLOPERATOR_H
diff --git a/src/qt3support/other/other.pri b/src/qt3support/other/other.pri
new file mode 100644
index 0000000..b2b0e56
--- /dev/null
+++ b/src/qt3support/other/other.pri
@@ -0,0 +1,24 @@
+# Qt compat module
+
+HEADERS += other/q3dropsite.h \
+ other/q3dragobject.h \
+ other/q3accel.h \
+ other/q3mimefactory.h \
+ other/q3polygonscanner.h \
+ other/q3process.h \
+ other/q3membuf_p.h \
+ other/q3boxlayout.h \
+ other/q3gridlayout.h
+
+SOURCES += other/q3dropsite.cpp \
+ other/q3dragobject.cpp \
+ other/q3accel.cpp \
+ other/q3mimefactory.cpp \
+ other/q3polygonscanner.cpp \
+ other/q3process.cpp \
+ other/q3membuf.cpp
+
+unix:SOURCES += other/q3process_unix.cpp
+win32:SOURCES+= other/q3process_win.cpp
+
+
diff --git a/src/qt3support/other/q3accel.cpp b/src/qt3support/other/q3accel.cpp
new file mode 100644
index 0000000..1dc8b2c
--- /dev/null
+++ b/src/qt3support/other/q3accel.cpp
@@ -0,0 +1,982 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3accel.h"
+
+#include "q3signal.h"
+#include "qapplication.h"
+#include "qwidget.h"
+#include "q3ptrlist.h"
+#include "qwhatsthis.h"
+#include "qpointer.h"
+#include "qstatusbar.h"
+#include "qdockwidget.h"
+#include "qevent.h"
+#include "qkeysequence.h"
+#include "private/qapplication_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+/*!
+ \class Q3Accel
+ \brief The Q3Accel class handles keyboard accelerator and shortcut keys.
+
+ \compat
+
+ A keyboard accelerator triggers an action when a certain key
+ combination is pressed. The accelerator handles all keyboard
+ activity for all the children of one top-level widget, so it is
+ not affected by the keyboard focus.
+
+ In most cases, you will not need to use this class directly. Use
+ the QAction class to create actions with accelerators that can be
+ used in both menus and toolbars. If you're only interested in
+ menus use Q3MenuData::insertItem() or Q3MenuData::setAccel() to make
+ accelerators for operations that are also available on menus. Many
+ widgets automatically generate accelerators, such as QAbstractButton,
+ QGroupBox, QLabel (with QLabel::setBuddy()), QMenuBar, and QTabBar.
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 0
+
+ A Q3Accel contains a list of accelerator items that can be
+ manipulated using insertItem(), removeItem(), clear(), key() and
+ findKey().
+
+ Each accelerator item consists of an identifier and a \l
+ QKeySequence. A single key sequence consists of a keyboard code
+ combined with modifiers (Qt::SHIFT, Qt::CTRL, Qt::ALT, or
+ Qt::UNICODE_ACCEL). For example, Qt::CTRL + Qt::Key_P could be a shortcut
+ for printing a document. As an alternative, use Qt::UNICODE_ACCEL with the
+ unicode code point of the character. For example, Qt::UNICODE_ACCEL
+ + 'A' gives the same accelerator as Qt::Key_A.
+
+ When an accelerator key is pressed, the accelerator sends out the
+ signal activated() with a number that identifies this particular
+ accelerator item. Accelerator items can also be individually
+ connected, so that two different keys will activate two different
+ slots (see connectItem() and disconnectItem()).
+
+ The activated() signal is \e not emitted when two or more
+ accelerators match the same key. Instead, the first matching
+ accelerator sends out the activatedAmbiguously() signal. By
+ pressing the key multiple times, users can navigate between all
+ matching accelerators. Some standard controls like QPushButton and
+ QCheckBox connect the activatedAmbiguously() signal to the
+ harmless setFocus() slot, whereas activated() is connected to a
+ slot invoking the button's action. Most controls, like QLabel and
+ QTabBar, treat activated() and activatedAmbiguously() as
+ equivalent.
+
+ Use setEnabled() to enable or disable all the items in an
+ accelerator, or setItemEnabled() to enable or disable individual
+ items. An item is active only when both the Q3Accel and the item
+ itself are enabled.
+
+ The function setWhatsThis() specifies a help text that appears
+ when the user presses an accelerator key in What's This mode.
+
+ The accelerator will be deleted when \e parent is deleted,
+ and will consume relevant key events until then.
+
+ Please note that the accelerator
+ \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 1
+ can be triggered with both the 'M' key, and with Shift+M,
+ unless a second accelerator is defined for the Shift+M
+ combination.
+
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 2
+
+ \sa QKeyEvent QWidget::keyPressEvent()
+ QAbstractButton::setAccel() QLabel::setBuddy() QKeySequence
+*/
+
+
+struct Q3AccelItem { // internal accelerator item
+ Q3AccelItem(const QKeySequence &k, int i)
+ { key=k; id=i; enabled=true; signal=0; }
+ ~Q3AccelItem() { delete signal; }
+ int id;
+ QKeySequence key;
+ bool enabled;
+ Q3Signal *signal;
+ QString whatsthis;
+};
+
+
+typedef Q3PtrList<Q3AccelItem> Q3AccelList; // internal accelerator list
+
+class Q3AccelPrivate {
+public:
+ Q3AccelPrivate(Q3Accel* p);
+ ~Q3AccelPrivate();
+ Q3AccelList aitems;
+ bool enabled;
+ QPointer<QWidget> watch;
+ bool ignorewhatsthis;
+ Q3Accel* parent;
+
+ void activate(Q3AccelItem* item);
+ void activateAmbiguously(Q3AccelItem* item);
+};
+
+class Q3AccelManager {
+public:
+ static Q3AccelManager* self() { return self_ptr ? self_ptr : new Q3AccelManager; }
+ void registerAccel(Q3AccelPrivate* a) { accels.append(a); }
+ void unregisterAccel(Q3AccelPrivate* a) { accels.removeRef(a); if (accels.isEmpty()) delete this; }
+ bool tryAccelEvent(QWidget* w, QKeyEvent* e);
+ bool dispatchAccelEvent(QWidget* w, QKeyEvent* e);
+ bool tryComposeUnicode(QWidget* w, QKeyEvent* e);
+
+private:
+ Q3AccelManager()
+ : currentState(QKeySequence::NoMatch), clash(-1), metaComposeUnicode(false),composedUnicode(0)
+ { setFuncPtr(); self_ptr = this; }
+ ~Q3AccelManager() { self_ptr = 0; }
+ void setFuncPtr();
+
+ bool correctSubWindow(QWidget *w, Q3AccelPrivate* d);
+ QKeySequence::SequenceMatch match(QKeyEvent* e, Q3AccelItem* item, QKeySequence& temp);
+ int translateModifiers(ButtonState state);
+
+ Q3PtrList<Q3AccelPrivate> accels;
+ static Q3AccelManager* self_ptr;
+ QKeySequence::SequenceMatch currentState;
+ QKeySequence intermediate;
+ int clash;
+ bool metaComposeUnicode;
+ int composedUnicode;
+};
+Q3AccelManager* Q3AccelManager::self_ptr = 0;
+
+bool Q_COMPAT_EXPORT qt_tryAccelEvent(QWidget* w, QKeyEvent* e){
+ return Q3AccelManager::self()->tryAccelEvent(w, e);
+}
+
+bool Q_COMPAT_EXPORT qt_dispatchAccelEvent(QWidget* w, QKeyEvent* e){
+ return Q3AccelManager::self()->dispatchAccelEvent(w, e);
+}
+
+bool Q_COMPAT_EXPORT qt_tryComposeUnicode(QWidget* w, QKeyEvent* e){
+ return Q3AccelManager::self()->tryComposeUnicode(w, e);
+}
+
+void Q3AccelManager::setFuncPtr() {
+ if (qApp->d_func()->qt_compat_used)
+ return;
+ QApplicationPrivate *data = static_cast<QApplicationPrivate*>(qApp->d_ptr.data());
+ data->qt_tryAccelEvent = qt_tryAccelEvent;
+ data->qt_tryComposeUnicode = qt_tryComposeUnicode;
+ data->qt_dispatchAccelEvent = qt_dispatchAccelEvent;
+ data->qt_compat_used = true;
+}
+
+#ifdef Q_WS_MAC
+static bool qt_accel_no_shortcuts = true;
+#else
+static bool qt_accel_no_shortcuts = false;
+#endif
+void Q_COMPAT_EXPORT qt_set_accel_auto_shortcuts(bool b) { qt_accel_no_shortcuts = b; }
+
+/*
+ \internal
+ Returns true if the accel is in the current subwindow, else false.
+*/
+bool Q3AccelManager::correctSubWindow(QWidget* w, Q3AccelPrivate* d) {
+#if !defined (Q_OS_MACX)
+ if (!d->watch || !d->watch->isVisible() || !d->watch->isEnabled())
+#else
+ if (!d->watch || (!d->watch->isVisible() && !d->watch->inherits("QMenuBar")) || !d->watch->isEnabled())
+#endif
+ return false;
+ QWidget* tlw = w->window();
+ QWidget* wtlw = d->watch->window();
+
+ /* if we live in a floating dock window, keep our parent's
+ * accelerators working */
+#ifndef QT_NO_MAINWINDOW
+ if ((tlw->windowType() == Qt::Dialog) && tlw->parentWidget() && qobject_cast<QDockWidget*>(tlw))
+ return tlw->parentWidget()->window() == wtlw;
+
+ if (wtlw != tlw)
+ return false;
+#endif
+ /* if we live in a MDI subwindow, ignore the event if we are
+ not the active document window */
+ QWidget* sw = d->watch;
+ while (sw && sw->windowType() != Qt::SubWindow)
+ sw = sw->parentWidget(true);
+ if (sw) { // we are in a subwindow indeed
+ QWidget* fw = w;
+ while (fw && fw != sw)
+ fw = fw->parentWidget(true);
+ if (fw != sw) // focus widget not in our subwindow
+ return false;
+ }
+ return true;
+}
+
+inline int Q3AccelManager::translateModifiers(ButtonState state)
+{
+ int result = 0;
+ if (state & ShiftButton)
+ result |= SHIFT;
+ if (state & ControlButton)
+ result |= CTRL;
+ if (state & MetaButton)
+ result |= META;
+ if (state & AltButton)
+ result |= ALT;
+ return result;
+}
+
+/*
+ \internal
+ Matches the current intermediate key sequence + the latest
+ keyevent, with and AccelItem. Returns Identical,
+ PartialMatch or NoMatch, and fills \a temp with the
+ resulting key sequence.
+*/
+QKeySequence::SequenceMatch Q3AccelManager::match(QKeyEvent *e, Q3AccelItem* item, QKeySequence& temp)
+{
+ QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
+ int index = intermediate.count();
+ temp = intermediate;
+
+ int modifier = translateModifiers(e->state());
+
+ if (e->key() && e->key() != Key_unknown) {
+ int key = e->key() | modifier;
+ if (e->key() == Key_BackTab) {
+ /*
+ In QApplication, we map shift+tab to shift+backtab.
+ This code here reverts the mapping in a way that keeps
+ backtab and shift+tab accelerators working, in that
+ order, meaning backtab has priority.*/
+ key &= ~SHIFT;
+
+ temp.setKey(key, index);
+ if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
+ return result;
+ if (e->state() & ShiftButton)
+ key |= SHIFT;
+ key = Key_Tab | (key & MODIFIER_MASK);
+ temp.setKey(key, index);
+ if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
+ return result;
+ } else {
+ temp.setKey(key, index);
+ if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
+ return result;
+ }
+
+ if (key == Key_BackTab) {
+ if (e->state() & ShiftButton)
+ key |= SHIFT;
+ temp.setKey(key, index);
+ if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
+ return result;
+ }
+ }
+ if (!e->text().isEmpty()) {
+ temp.setKey((int)e->text()[0].unicode() | UNICODE_ACCEL | modifier, index);
+ result = temp.matches(item->key);
+ }
+ return result;
+}
+
+bool Q3AccelManager::tryAccelEvent(QWidget* w, QKeyEvent* e)
+{
+ if (QKeySequence::NoMatch == currentState) {
+ e->t = QEvent::AccelOverride;
+ e->ignore();
+ QApplication::sendSpontaneousEvent(w, e);
+ if (e->isAccepted())
+ return false;
+ }
+ e->t = QEvent::Accel;
+ e->ignore();
+ QApplication::sendSpontaneousEvent(w, e);
+ return e->isAccepted();
+}
+
+bool Q3AccelManager::tryComposeUnicode(QWidget* w, QKeyEvent* e)
+{
+ if (metaComposeUnicode) {
+ int value = e->key() - Key_0;
+ // Ignore acceloverrides so we don't trigger
+ // accels on keypad when Meta compose is on
+ if ((e->type() == QEvent::AccelOverride) &&
+ (e->state() == Qt::Keypad + Qt::MetaButton)) {
+ e->accept();
+ // Meta compose start/continue
+ } else if ((e->type() == QEvent::KeyPress) &&
+ (e->state() == Qt::Keypad + Qt::MetaButton)) {
+ if (value >= 0 && value <= 9) {
+ composedUnicode *= 10;
+ composedUnicode += value;
+ return true;
+ } else {
+ // Composing interrupted, dispatch!
+ if (composedUnicode) {
+ QChar ch(composedUnicode);
+ QString s(ch);
+ QKeyEvent kep(QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s);
+ QKeyEvent ker(QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s);
+ QApplication::sendEvent(w, &kep);
+ QApplication::sendEvent(w, &ker);
+ }
+ composedUnicode = 0;
+ return true;
+ }
+ // Meta compose end, dispatch
+ } else if ((e->type() == QEvent::KeyRelease) &&
+ (e->key() == Key_Meta) &&
+ (composedUnicode != 0)) {
+ if ((composedUnicode > 0) &&
+ (composedUnicode < 0xFFFE)) {
+ QChar ch(composedUnicode);
+ QString s(ch);
+ QKeyEvent kep(QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s);
+ QKeyEvent ker(QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s);
+ QApplication::sendEvent(w, &kep);
+ QApplication::sendEvent(w, &ker);
+ }
+ composedUnicode = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ \internal
+ Checks for possible accelerators, if no widget
+ ate the keypres, or we are in the middle of a
+ partial key sequence.
+*/
+bool Q3AccelManager::dispatchAccelEvent(QWidget* w, QKeyEvent* e)
+{
+#ifndef QT_NO_STATUSBAR
+ // Needs to be declared and used here because of "goto doclash"
+ QStatusBar* mainStatusBar = 0;
+#endif
+
+ // Modifiers can NOT be accelerators...
+ if (e->key() >= Key_Shift &&
+ e->key() <= Key_Alt)
+ return false;
+
+ QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
+ QKeySequence tocheck, partial;
+ Q3AccelPrivate* accel = 0;
+ Q3AccelItem* item = 0;
+ Q3AccelPrivate* firstaccel = 0;
+ Q3AccelItem* firstitem = 0;
+ Q3AccelPrivate* lastaccel = 0;
+ Q3AccelItem* lastitem = 0;
+
+ QKeyEvent pe = *e;
+ int n = -1;
+ int hasShift = (e->state()&Qt::ShiftButton)?1:0;
+ bool identicalDisabled = false;
+ bool matchFound = false;
+ do {
+ accel = accels.first();
+ matchFound = false;
+ while (accel) {
+ if (correctSubWindow(w, accel)) {
+ if (accel->enabled) {
+ item = accel->aitems.last();
+ while(item) {
+ if (QKeySequence::Identical == (result = match(&pe, item, tocheck))) {
+ if (item->enabled) {
+ if (!firstaccel) {
+ firstaccel = accel;
+ firstitem = item;
+ }
+ lastaccel = accel;
+ lastitem = item;
+ n++;
+ matchFound = true;
+ if (n > QMAX(clash,0))
+ goto doclash;
+ } else {
+ identicalDisabled = true;
+ }
+ }
+ if (item->enabled && QKeySequence::PartialMatch == result) {
+ partial = tocheck;
+ matchFound = true;
+ }
+ item = accel->aitems.prev();
+ }
+ } else {
+ item = accel->aitems.last();
+ while(item) {
+ if (QKeySequence::Identical == match(&pe, item, tocheck))
+ identicalDisabled = true;
+ item = accel->aitems.prev();
+ }
+ }
+ }
+ accel = accels.next();
+ }
+ pe = QKeyEvent(QEvent::Accel, pe.key(), pe.ascii(), pe.state()&~Qt::ShiftButton, pe.text());
+ } while (hasShift-- && !matchFound && !identicalDisabled);
+
+#ifndef QT_NO_STATUSBAR
+ mainStatusBar = (QStatusBar*) w->window()->child(0, "QStatusBar");
+#endif
+ if (n < 0) { // no match found
+ currentState = partial.count() ? QKeySequence::PartialMatch : QKeySequence::NoMatch;
+#ifndef QT_NO_STATUSBAR
+ // Only display message if we are, or were, in a partial match
+ if (mainStatusBar && (QKeySequence::PartialMatch == currentState || intermediate.count())) {
+ if (currentState == QKeySequence::PartialMatch) {
+ mainStatusBar->showMessage((QString)partial + QLatin1String(", ..."));
+ } else if (!identicalDisabled) {
+ QString message = Q3Accel::tr("%1, %2 not defined").
+ arg((QString)intermediate).
+ arg(QKeySequence::encodeString(e->key() | translateModifiers(e->state())));
+ mainStatusBar->showMessage(message, 2000);
+ // Since we're a NoMatch, reset the clash count
+ clash = -1;
+ } else {
+ mainStatusBar->clearMessage();
+ }
+ }
+#endif
+
+ bool eatKey = (QKeySequence::PartialMatch == currentState || intermediate.count());
+ intermediate = partial;
+ if (eatKey)
+ e->accept();
+ return eatKey;
+ } else if (n == 0) { // found exactly one match
+ clash = -1; // reset
+#ifndef QT_NO_STATUSBAR
+ if (currentState == QKeySequence::PartialMatch && mainStatusBar)
+ mainStatusBar->clearMessage();
+#endif
+ currentState = QKeySequence::NoMatch; // Free sequence keylock
+ intermediate = QKeySequence();
+ lastaccel->activate(lastitem);
+ e->accept();
+ return true;
+ }
+
+ doclash: // found more than one match
+#ifndef QT_NO_STATUSBAR
+ if (!mainStatusBar) // if "goto doclash", we need to get status bar again.
+ mainStatusBar = (QStatusBar*) w->window()->child(0, "QStatusBar");
+#endif
+
+ QString message = Q3Accel::tr("Ambiguous %1 not handled").arg((QString)tocheck);
+ if (clash >= 0 && n > clash) { // pick next match
+ intermediate = QKeySequence();
+ currentState = QKeySequence::NoMatch; // Free sequence keylock
+ clash++;
+#ifndef QT_NO_STATUSBAR
+ if (mainStatusBar &&
+ !lastitem->signal &&
+ !(lastaccel->parent->receivers(SIGNAL(activatedAmbiguously(int)))))
+ mainStatusBar->showMessage(message, 2000);
+#endif
+ lastaccel->activateAmbiguously(lastitem);
+ } else { // start (or wrap) with the first matching
+ intermediate = QKeySequence();
+ currentState = QKeySequence::NoMatch; // Free sequence keylock
+ clash = 0;
+#ifndef QT_NO_STATUSBAR
+ if (mainStatusBar &&
+ !firstitem->signal &&
+ !(firstaccel->parent->receivers(SIGNAL(activatedAmbiguously(int)))))
+ mainStatusBar->showMessage(message, 2000);
+#endif
+ firstaccel->activateAmbiguously(firstitem);
+ }
+ e->accept();
+ return true;
+}
+
+Q3AccelPrivate::Q3AccelPrivate(Q3Accel* p)
+ : parent(p)
+{
+ Q3AccelManager::self()->registerAccel(this);
+ aitems.setAutoDelete(true);
+ ignorewhatsthis = false;
+}
+
+Q3AccelPrivate::~Q3AccelPrivate()
+{
+ Q3AccelManager::self()->unregisterAccel(this);
+}
+
+static Q3AccelItem *find_id(Q3AccelList &list, int id)
+{
+ register Q3AccelItem *item = list.first();
+ while (item && item->id != id)
+ item = list.next();
+ return item;
+}
+
+static Q3AccelItem *find_key(Q3AccelList &list, const QKeySequence &key)
+{
+ register Q3AccelItem *item = list.first();
+ while (item && !(item->key == key))
+ item = list.next();
+ return item;
+}
+
+/*!
+ Constructs a Q3Accel object called \a name, with parent \a parent.
+ The accelerator operates on \a parent.
+*/
+
+Q3Accel::Q3Accel(QWidget *parent, const char *name)
+ : QObject(parent, name)
+{
+ d = new Q3AccelPrivate(this);
+ d->enabled = true;
+ d->watch = parent;
+#if defined(QT_CHECK_NULL)
+ if (!d->watch)
+ qWarning("Q3Accel: An accelerator must have a parent or a watch widget");
+#endif
+}
+
+/*!
+ Constructs a Q3Accel object called \a name, that operates on \a
+ watch, and is a child of \a parent.
+
+ This constructor is not needed for normal application programming.
+*/
+Q3Accel::Q3Accel(QWidget* watch, QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+ d = new Q3AccelPrivate(this);
+ d->enabled = true;
+ d->watch = watch;
+#if defined(QT_CHECK_NULL)
+ if (!d->watch)
+ qWarning("Q3Accel: An accelerator must have a parent or a watch widget");
+#endif
+}
+
+/*!
+ Destroys the accelerator object and frees all allocated resources.
+*/
+
+Q3Accel::~Q3Accel()
+{
+ delete d;
+}
+
+
+/*!
+ \fn void Q3Accel::activated(int id)
+
+ This signal is emitted when the user types the shortcut's key
+ sequence. \a id is a number that identifies this particular
+ accelerator item.
+
+ \sa activatedAmbiguously()
+*/
+
+/*!
+ \fn void Q3Accel::activatedAmbiguously(int id)
+
+ This signal is emitted when the user types a shortcut key
+ sequence that is ambiguous. For example, if one key sequence is a
+ "prefix" for another and the user types these keys it isn't clear
+ if they want the shorter key sequence, or if they're about to
+ type more to complete the longer key sequence. \a id is a number
+ that identifies this particular accelerator item.
+
+ \sa activated()
+*/
+
+/*!
+ Returns true if the accelerator is enabled; otherwise returns
+ false.
+
+ \sa setEnabled(), isItemEnabled()
+*/
+
+bool Q3Accel::isEnabled() const
+{
+ return d->enabled;
+}
+
+
+/*!
+ Enables the accelerator if \a enable is true, or disables it if \a
+ enable is false.
+
+ Individual keys can also be enabled or disabled using
+ setItemEnabled(). To work, a key must be an enabled item in an
+ enabled Q3Accel.
+
+ \sa isEnabled(), setItemEnabled()
+*/
+
+void Q3Accel::setEnabled(bool enable)
+{
+ d->enabled = enable;
+}
+
+
+/*!
+ Returns the number of accelerator items in this accelerator.
+*/
+
+uint Q3Accel::count() const
+{
+ return d->aitems.count();
+}
+
+
+static int get_seq_id()
+{
+ static int seq_no = -2; // -1 is used as return value in findKey()
+ return seq_no--;
+}
+
+/*!
+ Inserts an accelerator item and returns the item's identifier.
+
+ \a key is a key code and an optional combination of SHIFT, CTRL
+ and ALT. \a id is the accelerator item id.
+
+ If \a id is negative, then the item will be assigned a unique
+ negative identifier less than -1.
+
+ \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 3
+*/
+
+int Q3Accel::insertItem(const QKeySequence& key, int id)
+{
+ if (id == -1)
+ id = get_seq_id();
+ d->aitems.insert(0, new Q3AccelItem(key,id));
+ return id;
+}
+
+/*!
+ Removes the accelerator item with the identifier \a id.
+*/
+
+void Q3Accel::removeItem(int id)
+{
+ if (find_id(d->aitems, id))
+ d->aitems.remove();
+}
+
+
+/*!
+ Removes all accelerator items.
+*/
+
+void Q3Accel::clear()
+{
+ d->aitems.clear();
+}
+
+
+/*!
+ Returns the key sequence of the accelerator item with identifier
+ \a id, or an invalid key sequence (0) if the id cannot be found.
+*/
+
+QKeySequence Q3Accel::key(int id)
+{
+ Q3AccelItem *item = find_id(d->aitems, id);
+ return item ? item->key : QKeySequence(0);
+}
+
+
+/*!
+ Returns the identifier of the accelerator item with the key code
+ \a key, or -1 if the item cannot be found.
+*/
+
+int Q3Accel::findKey(const QKeySequence& key) const
+{
+ Q3AccelItem *item = find_key(d->aitems, key);
+ return item ? item->id : -1;
+}
+
+
+/*!
+ Returns true if the accelerator item with the identifier \a id is
+ enabled. Returns false if the item is disabled or cannot be found.
+
+ \sa setItemEnabled(), isEnabled()
+*/
+
+bool Q3Accel::isItemEnabled(int id) const
+{
+ Q3AccelItem *item = find_id(d->aitems, id);
+ return item ? item->enabled : false;
+}
+
+
+/*!
+ Enables the accelerator item with the identifier \a id if \a
+ enable is true, and disables item \a id if \a enable is false.
+
+ To work, an item must be enabled and be in an enabled Q3Accel.
+
+ \sa isItemEnabled(), isEnabled()
+*/
+
+void Q3Accel::setItemEnabled(int id, bool enable)
+{
+ Q3AccelItem *item = find_id(d->aitems, id);
+ if (item)
+ item->enabled = enable;
+}
+
+
+/*!
+ Connects the accelerator item \a id to the slot \a member of \a
+ receiver. Returns true if the connection is successful.
+
+ \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 4
+
+ Of course, you can also send a signal as \a member.
+
+ Normally accelerators are connected to slots which then receive
+ the \c activated(int id) signal with the id of the accelerator
+ item that was activated. If you choose to connect a specific
+ accelerator item using this function, the \c activated() signal is
+ emitted if the associated key sequence is pressed but no \c
+ activated(int id) signal is emitted.
+
+ \sa disconnectItem(), QObject::connect()
+*/
+
+bool Q3Accel::connectItem(int id, const QObject *receiver, const char *member)
+{
+ Q3AccelItem *item = find_id(d->aitems, id);
+ if (item) {
+ if (!item->signal) {
+ item->signal = new Q3Signal;
+ Q_CHECK_PTR(item->signal);
+ }
+ return item->signal->connect(receiver, member);
+ }
+ return false;
+}
+
+/*!
+ Disconnects the accelerator item identified by \a id from
+ the function called \a member in the \a receiver object.
+ Returns true if the connection existed and the disconnect
+ was successful.
+
+ \sa connectItem(), QObject::disconnect()
+*/
+
+bool Q3Accel::disconnectItem(int id, const QObject *receiver,
+ const char *member)
+{
+ Q3AccelItem *item = find_id(d->aitems, id);
+ if (item && item->signal)
+ return item->signal->disconnect(receiver, member);
+ return false;
+}
+
+void Q3AccelPrivate::activate(Q3AccelItem* item)
+{
+#ifndef QT_NO_WHATSTHIS
+ if (QWhatsThis::inWhatsThisMode() && !ignorewhatsthis) {
+ QWhatsThis::showText(QCursor::pos(), item->whatsthis);
+ return;
+ }
+#endif
+ if (item->signal)
+ item->signal->activate();
+ else
+ emit parent->activated(item->id);
+}
+
+void Q3AccelPrivate::activateAmbiguously(Q3AccelItem* item)
+{
+ if (item->signal)
+ item->signal->activate();
+ else
+ emit parent->activatedAmbiguously(item->id);
+}
+
+
+/*!
+ Returns the shortcut key sequence for \a str, or an invalid key
+ sequence (0) if \a str has no shortcut sequence.
+
+ For example, shortcutKey("E&xit") returns QKeySequence(Qt::ALT +
+ Qt::Key_X), shortcutKey("&Quit") returns QKeySequence(Qt::ALT +
+ Qt::Key_Q), and shortcutKey("Quit") returns QKeySequence().
+*/
+
+QKeySequence Q3Accel::shortcutKey(const QString &str)
+{
+ if(qt_accel_no_shortcuts)
+ return QKeySequence();
+
+ int p = 0;
+ while (p >= 0) {
+ p = str.find(QLatin1Char('&'), p) + 1;
+ if (p <= 0 || p >= (int)str.length())
+ return 0;
+ if (str[p] != QLatin1Char('&')) {
+ QChar c = str[p];
+ if (c.isPrint()) {
+ char ltr = c.upper().latin1();
+ if (ltr >= (char)Key_A && ltr <= (char)Key_Z)
+ c = QLatin1Char(ltr);
+ else
+ c = c.lower();
+ return QKeySequence(c.unicode() + ALT + UNICODE_ACCEL);
+ }
+ }
+ p++;
+ }
+ return QKeySequence();
+}
+
+/*! \obsolete
+
+ Creates an accelerator string for the key \a k.
+ For instance CTRL+Key_O gives "Ctrl+O". The "Ctrl" etc.
+ are translated (using QObject::tr()) in the "Q3Accel" context.
+
+ The function is superfluous. Cast the QKeySequence \a k to a
+ QString for the same effect.
+*/
+QString Q3Accel::keyToString(QKeySequence k)
+{
+ return (QString) k;
+}
+
+/*!\obsolete
+
+ Returns an accelerator code for the string \a s. For example
+ "Ctrl+O" gives CTRL+UNICODE_ACCEL+'O'. The strings "Ctrl",
+ "Shift", "Alt" are recognized, as well as their translated
+ equivalents in the "Q3Accel" context (using QObject::tr()). Returns 0
+ if \a s is not recognized.
+
+ This function is typically used with \link QObject::tr() tr
+ \endlink(), so that accelerator keys can be replaced in
+ translations:
+
+ \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 5
+
+ Notice the "File|Open" translator comment. It is by no means
+ necessary, but it provides some context for the human translator.
+
+ The function is superfluous. Construct a QKeySequence from the
+ string \a s for the same effect.
+
+ \sa QObject::tr(), {Internationalization with Qt}
+*/
+QKeySequence Q3Accel::stringToKey(const QString & s)
+{
+ return QKeySequence(s);
+}
+
+
+/*!
+ Sets a What's This help text for the accelerator item \a id to \a
+ text.
+
+ The text will be shown when the application is in What's This mode
+ and the user hits the accelerator key.
+
+ To set What's This help on a menu item (with or without an
+ accelerator key), use Q3MenuData::setWhatsThis().
+
+ \sa whatsThis(), QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis()
+*/
+void Q3Accel::setWhatsThis(int id, const QString& text)
+{
+ Q3AccelItem *item = find_id(d->aitems, id);
+ if (item)
+ item->whatsthis = text;
+}
+
+/*!
+ Returns the What's This help text for the specified item \a id or
+ an empty string if no text has been specified.
+
+ \sa setWhatsThis()
+*/
+QString Q3Accel::whatsThis(int id) const
+{
+
+ Q3AccelItem *item = find_id(d->aitems, id);
+ return item? item->whatsthis : QString();
+}
+
+/*!\internal */
+void Q3Accel::setIgnoreWhatsThis(bool b)
+{
+ d->ignorewhatsthis = b;
+}
+
+/*!\internal */
+bool Q3Accel::ignoreWhatsThis() const
+{
+ return d->ignorewhatsthis;
+}
+
+/*!
+ \fn void Q3Accel::repairEventFilter()
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/other/q3accel.h b/src/qt3support/other/q3accel.h
new file mode 100644
index 0000000..cb7f367
--- /dev/null
+++ b/src/qt3support/other/q3accel.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3ACCEL_H
+#define Q3ACCEL_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qkeysequence.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3AccelPrivate;
+
+class Q_COMPAT_EXPORT Q3Accel : public QObject // accelerator class
+{
+ Q_OBJECT
+public:
+ Q3Accel( QWidget *parent, const char *name=0 );
+ Q3Accel( QWidget* watch, QObject *parent, const char *name=0 );
+ ~Q3Accel();
+
+ bool isEnabled() const;
+ void setEnabled( bool );
+
+ uint count() const;
+
+ int insertItem( const QKeySequence& key, int id=-1);
+ void removeItem( int id );
+ void clear();
+
+ QKeySequence key( int id );
+ int findKey( const QKeySequence& key ) const;
+
+ bool isItemEnabled( int id ) const;
+ void setItemEnabled( int id, bool enable );
+
+ bool connectItem( int id, const QObject *receiver, const char* member );
+ bool disconnectItem( int id, const QObject *receiver, const char* member );
+
+ void repairEventFilter() {}
+
+ void setWhatsThis( int id, const QString& );
+ QString whatsThis( int id ) const;
+ void setIgnoreWhatsThis( bool );
+ bool ignoreWhatsThis() const;
+
+ static QKeySequence shortcutKey( const QString & );
+ static QString keyToString(QKeySequence k );
+ static QKeySequence stringToKey( const QString & );
+
+Q_SIGNALS:
+ void activated( int id );
+ void activatedAmbiguously( int id );
+
+private:
+ Q3AccelPrivate * d;
+
+private:
+ Q_DISABLE_COPY(Q3Accel)
+ friend class Q3AccelPrivate;
+ friend class Q3AccelManager;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3ACCEL_H
diff --git a/src/qt3support/other/q3boxlayout.cpp b/src/qt3support/other/q3boxlayout.cpp
new file mode 100644
index 0000000..15e5d2a
--- /dev/null
+++ b/src/qt3support/other/q3boxlayout.cpp
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3HBoxLayout
+ \compat
+
+ \brief The Q3HBoxLayout class lines up widgets horizontally.
+ \sa Q3VBoxLayout
+*/
+
+/*!
+ \fn Q3HBoxLayout::Q3HBoxLayout(QWidget *parent, int margin = 0, int spacing = -1, const char *name = 0)
+
+ Constructs a new top-level horizontal box called \a name, with parent
+ \a parent. The \a margin is the number of pixels between the edge of the
+ widget and its managed children. The \a spacing is the default number of
+ pixels between neighboring children. If \a spacing is -1 the value of margin
+ is used for spacing.
+*/
+
+/*!
+ \fn Q3HBoxLayout::Q3HBoxLayout(QLayout *parentLayout, int spacing = -1, const char *name = 0)
+
+ Constructs a new horizontal box called \a name and adds it to
+ \a parentLayout. The \a spacing is the default number of pixels between
+ neighboring children. If \a spacing is -1, this Q3HBoxLayout will inherit
+ its parent's spacing.
+*/
+
+/*!
+ \fn Q3HBoxLayout::Q3HBoxLayout(int spacing = -1, const char *name = 0)
+
+ Constructs a new horizontal box called \a name. You must add it to another
+ layout. The \a spacing is the default number of pixels between neighboring
+ children. If \a spacing is -1, this QHBoxLayout will inherit its parent's
+ spacing().
+*/
+
+/*!
+ \fn Q3HBoxLayout::Q3HBoxLayout()
+ \internal
+*/
+
+/*!
+ \fn Q3HBoxLayout::Q3HBoxLayout(QWidget *parent)
+ \internal
+*/
+
+/*!
+ \class Q3VBoxLayout
+ \compat
+
+ \brief The Q3VBoxLayout class lines up widgets vertically.
+ \sa Q3HBoxLayout
+*/
+
+/*!
+ \fn Q3VBoxLayout::Q3VBoxLayout(QWidget *parent, int margin = 0, int spacing = -1, const char *name = 0)
+
+ Constructs a new top-level vertical box called \a name, with parent
+ \a parent. The \a margin is the number of pixels between the edge of the
+ widget and its managed children. The \a spacing is the default number of
+ pixels between neighboring children. If \a spacing is -1 the value of
+ margin is used for spacing.
+*/
+
+/*!
+ \fn Q3VBoxLayout::Q3VBoxLayout(QLayout *parentLayout, int spacing = -1, const char *name = 0)
+
+ Constructs a new vertical box called \a name and adds it to
+ \a parentLayout. The \a spacing is the default number of pixels between
+ neighboring children. If \a spacing is -1, this Q3VBoxLayout will inherit
+ its parent's spacing().
+*/
+
+/*!
+ \fn Q3VBoxLayout::Q3VBoxLayout(int spacing = -1, const char *name = 0)
+
+ Constructs a new vertical box called \a name. You must add it to another
+ layout. The \a spacing is the default number of pixels between neighboring
+ children. If \a spacing is -1, this Q3VBoxLayout will inherit its parent's
+ spacing().
+*/
+
+/*!
+ \fn Q3VBoxLayout::Q3VBoxLayout()
+ \internal
+*/
+
+/*!
+ \fn Q3VBoxLayout::Q3VBoxLayout(QWidget *parent)
+ \internal
+*/
diff --git a/src/qt3support/other/q3boxlayout.h b/src/qt3support/other/q3boxlayout.h
new file mode 100644
index 0000000..c5aeea3
--- /dev/null
+++ b/src/qt3support/other/q3boxlayout.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3BOXLAYOUT_H
+#define Q3BOXLAYOUT_H
+
+#include <QtGui/qboxlayout.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3BoxLayout : public QBoxLayout
+{
+public:
+ inline explicit Q3BoxLayout(Direction dir, QWidget *parent = 0)
+ : QBoxLayout(dir, parent) { setMargin(0); setSpacing(0); }
+
+ inline Q3BoxLayout(QWidget *parent, Direction dir, int margin = 0, int spacing = -1,
+ const char *name = 0)
+ : QBoxLayout(parent, dir, margin, spacing, name) {}
+
+ inline Q3BoxLayout(QLayout *parentLayout, Direction dir, int spacing = -1,
+ const char *name = 0)
+ : QBoxLayout(parentLayout, dir, spacing, name) { setMargin(0); }
+
+ inline Q3BoxLayout(Direction dir, int spacing, const char *name = 0)
+ : QBoxLayout(dir, spacing, name) { setMargin(0); }
+
+private:
+ Q_DISABLE_COPY(Q3BoxLayout)
+};
+
+class Q3HBoxLayout : public Q3BoxLayout
+{
+public:
+ inline Q3HBoxLayout() : Q3BoxLayout(LeftToRight) {}
+
+ inline explicit Q3HBoxLayout(QWidget *parent) : Q3BoxLayout(LeftToRight, parent) {}
+
+ inline Q3HBoxLayout(QWidget *parent, int margin,
+ int spacing = -1, const char *name = 0)
+ : Q3BoxLayout(parent, LeftToRight, margin, spacing, name) {}
+
+ inline Q3HBoxLayout(QLayout *parentLayout,
+ int spacing = -1, const char *name = 0)
+ : Q3BoxLayout(parentLayout, LeftToRight, spacing, name) {}
+
+ inline Q3HBoxLayout(int spacing, const char *name = 0)
+ : Q3BoxLayout(LeftToRight, spacing, name) {}
+
+private:
+ Q_DISABLE_COPY(Q3HBoxLayout)
+};
+
+class Q3VBoxLayout : public Q3BoxLayout
+{
+public:
+ inline Q3VBoxLayout() : Q3BoxLayout(TopToBottom) {}
+
+ inline explicit Q3VBoxLayout(QWidget *parent) : Q3BoxLayout(TopToBottom, parent) {}
+
+ inline Q3VBoxLayout(QWidget *parent, int margin,
+ int spacing = -1, const char *name = 0)
+ : Q3BoxLayout(parent, TopToBottom, margin, spacing, name) {}
+
+ inline Q3VBoxLayout(QLayout *parentLayout,
+ int spacing = -1, const char *name = 0)
+ : Q3BoxLayout(parentLayout, TopToBottom, spacing, name) {}
+
+ inline Q3VBoxLayout(int spacing, const char *name = 0)
+ : Q3BoxLayout(TopToBottom, spacing, name) {}
+
+private:
+ Q_DISABLE_COPY(Q3VBoxLayout)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3BOXLAYOUT_H
diff --git a/src/qt3support/other/q3dragobject.cpp b/src/qt3support/other/q3dragobject.cpp
new file mode 100644
index 0000000..98891ab
--- /dev/null
+++ b/src/qt3support/other/q3dragobject.cpp
@@ -0,0 +1,1567 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#ifndef QT_NO_MIME
+
+#include "q3dragobject.h"
+#include "qpixmap.h"
+#include "qevent.h"
+#include "qfile.h"
+#include "qtextcodec.h"
+#include "qapplication.h"
+#include "qpoint.h"
+#include "qwidget.h"
+#include "qbuffer.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qimage.h"
+#include "qregexp.h"
+#include "qdir.h"
+#include "qdrag.h"
+#include "q3strlist.h"
+#include "q3cstring.h"
+
+#include <private/qobject_p.h>
+
+#include <ctype.h>
+#if defined(Q_OS_WINCE)
+#include <winsock.h>
+#include "qfunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static QWidget *last_target = 0;
+
+class QDragMime;
+
+class Q3DragObjectPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(Q3DragObject)
+public:
+ Q3DragObjectPrivate(): hot(0,0),pm_cursor(0) {}
+ QPixmap pixmap;
+ QPoint hot;
+ // store default cursors
+ QPixmap *pm_cursor;
+};
+
+class Q3TextDragPrivate : public Q3DragObjectPrivate
+{
+ Q_DECLARE_PUBLIC(Q3TextDrag)
+public:
+ Q3TextDragPrivate() { setSubType(QLatin1String("plain")); }
+ void setSubType(const QString & st) {
+ subtype = st;
+ fmt = "text/" + subtype.toLatin1();
+ }
+
+ QString txt;
+ QString subtype;
+ QByteArray fmt;
+};
+
+class Q3StoredDragPrivate : public Q3DragObjectPrivate
+{
+ Q_DECLARE_PUBLIC(Q3StoredDrag)
+public:
+ Q3StoredDragPrivate() {}
+ const char* fmt;
+ QByteArray enc;
+};
+
+class Q3ImageDragPrivate : public Q3DragObjectPrivate
+{
+ Q_DECLARE_PUBLIC(Q3ImageDrag)
+public:
+ QImage img;
+ QList<QByteArray> ofmts;
+};
+
+class QDragMime : public QMimeData
+{
+public:
+ QDragMime(Q3DragObject *parent) : QMimeData(), dragObject(parent) { }
+ ~QDragMime();
+
+ QByteArray data(const QString &mimetype) const;
+ bool hasFormat(const QString &mimetype) const;
+ QStringList formats() const;
+
+ QPointer<Q3DragObject> dragObject;
+};
+
+QDragMime::~QDragMime()
+{
+ delete dragObject;
+}
+QByteArray QDragMime::data(const QString &mimetype) const
+{
+ return dragObject->encodedData(mimetype.latin1());
+}
+
+bool QDragMime::hasFormat(const QString &mimetype) const
+{
+ return dragObject->provides(mimetype.latin1());
+}
+
+QStringList QDragMime::formats() const
+{
+ int i = 0;
+ const char *format;
+ QStringList f;
+ while ((format = dragObject->format(i))) {
+ f.append(QLatin1String(format));
+ ++i;
+ }
+ return f;
+}
+
+/*!
+ Constructs a drag object called \a name with a parent \a
+ dragSource.
+
+ Note that the drag object will be deleted when the \a dragSource is
+ deleted.
+*/
+
+Q3DragObject::Q3DragObject(QWidget * dragSource, const char * name)
+ : QObject(*(new Q3DragObjectPrivate), dragSource)
+{
+ setObjectName(QLatin1String(name));
+}
+
+/*! \internal */
+Q3DragObject::Q3DragObject(Q3DragObjectPrivate &dd, QWidget *dragSource)
+ : QObject(dd, dragSource)
+{
+}
+
+/*!
+ Destroys the drag object, canceling any drag and drop operation in
+ which it is involved.
+*/
+
+Q3DragObject::~Q3DragObject()
+{
+}
+
+#ifndef QT_NO_DRAGANDDROP
+/*!
+ Set the pixmap, \a pm, to display while dragging the object. The
+ platform-specific implementation will use this where it can - so
+ provide a small masked pixmap, and do not assume that the user
+ will actually see it.
+
+ The \a hotspot is the point on (or off) the pixmap that should be
+ under the cursor as it is dragged. It is relative to the top-left
+ pixel of the pixmap.
+
+ \warning We have seen problems with drag cursors on different
+ graphics hardware and driver software on Windows. Setting the
+ graphics acceleration in the display settings down one tick solved
+ the problems in all cases.
+*/
+void Q3DragObject::setPixmap(QPixmap pm, const QPoint& hotspot)
+{
+ Q_D(Q3DragObject);
+ d->pixmap = pm;
+ d->hot = hotspot;
+}
+
+/*!
+ \overload
+
+ Uses a hotspot that positions the pixmap below and to the right of
+ the mouse pointer. This allows the user to clearly see the point
+ on the window where they are dragging the data.
+*/
+void Q3DragObject::setPixmap(QPixmap pm)
+{
+ setPixmap(pm,QPoint(-10, -10));
+}
+
+/*!
+ Returns the currently set pixmap, or a null pixmap if none is set.
+
+ \sa QPixmap::isNull()
+*/
+QPixmap Q3DragObject::pixmap() const
+{
+ return d_func()->pixmap;
+}
+
+/*!
+ Returns the currently set pixmap hotspot.
+
+ \sa setPixmap()
+*/
+QPoint Q3DragObject::pixmapHotSpot() const
+{
+ return d_func()->hot;
+}
+
+/*!
+ Starts a drag operation using the contents of this object, using
+ DragDefault mode.
+
+ The function returns true if the caller should delete the original
+ copy of the dragged data (but see target()); otherwise returns
+ false.
+
+ If the drag contains \e references to information (e.g. file names
+ in a Q3UriDrag are references) then the return value should always
+ be ignored, as the target is expected to directly manipulate the
+ content referred to by the drag object. On X11 the return value should
+ always be correct anyway, but on Windows this is not necessarily
+ the case; e.g. the file manager starts a background process to
+ move files, so the source \e{must not} delete the files!
+
+ Note that on Windows the drag operation will start a blocking modal
+ event loop that will not dispatch any QTimers.
+*/
+bool Q3DragObject::drag()
+{
+ return drag(DragDefault);
+}
+
+/*!
+ After the drag completes, this function will return the QWidget
+ which received the drop, or 0 if the data was dropped on another
+ application.
+
+ This can be useful for detecting the case where drag and drop is
+ to and from the same widget.
+*/
+QWidget *Q3DragObject::target()
+{
+ return last_target;
+}
+
+/*!
+ Starts a drag operation using the contents of this object, using
+ \c DragMove mode. Be sure to read the constraints described in
+ drag().
+
+ Returns true if the data was dragged as a \e move, indicating that
+ the caller should remove the original source of the data (the drag
+ object must continue to have a copy); otherwise returns false.
+
+ \sa drag() dragCopy() dragLink()
+*/
+bool Q3DragObject::dragMove()
+{
+ return drag(DragMove);
+}
+
+
+/*!
+ Starts a drag operation using the contents of this object, using
+ \c DragCopy mode. Be sure to read the constraints described in
+ drag().
+
+ \sa drag() dragMove() dragLink()
+*/
+void Q3DragObject::dragCopy()
+{
+ (void)drag(DragCopy);
+}
+
+/*!
+ Starts a drag operation using the contents of this object, using
+ \c DragLink mode. Be sure to read the constraints described in
+ drag().
+
+ \sa drag() dragCopy() dragMove()
+*/
+void Q3DragObject::dragLink()
+{
+ (void)drag(DragLink);
+}
+
+
+/*!
+ \enum Q3DragObject::DragMode
+
+ This enum describes the possible drag modes.
+
+ \value DragDefault The mode is determined heuristically.
+ \value DragCopy The data is copied.
+ \value DragMove The data is moved.
+ \value DragLink The data is linked.
+ \value DragCopyOrMove The user chooses the mode by using the
+ \key{Shift} key to switch from the default
+ copy mode to move mode.
+*/
+
+
+/*!
+ \overload
+ Starts a drag operation using the contents of this object.
+
+ At this point, the object becomes owned by Qt, not the
+ application. You should not delete the drag object or anything it
+ references. The actual transfer of data to the target application
+ will be done during future event processing - after that time the
+ drag object will be deleted.
+
+ Returns true if the dragged data was dragged as a \e move,
+ indicating that the caller should remove the original source of
+ the data (the drag object must continue to have a copy); otherwise
+ returns false.
+
+ The \a mode specifies the drag mode (see
+ \l{Q3DragObject::DragMode}.) Normally one of the simpler drag(),
+ dragMove(), or dragCopy() functions would be used instead.
+*/
+bool Q3DragObject::drag(DragMode mode)
+{
+ Q_D(Q3DragObject);
+ QDragMime *data = new QDragMime(this);
+ int i = 0;
+ const char *fmt;
+ while ((fmt = format(i))) {
+ data->setData(QLatin1String(fmt), encodedData(fmt));
+ ++i;
+ }
+
+ QDrag *drag = new QDrag(qobject_cast<QWidget *>(parent()));
+ drag->setMimeData(data);
+ drag->setPixmap(d->pixmap);
+ drag->setHotSpot(d->hot);
+
+ Qt::DropActions allowedOps;
+ Qt::DropAction defaultOp = Qt::IgnoreAction;
+ switch(mode) {
+ default:
+ case DragDefault:
+ case DragCopyOrMove:
+ allowedOps = Qt::CopyAction|Qt::MoveAction;
+ defaultOp = Qt::IgnoreAction;
+ break;
+ case DragCopy:
+ allowedOps = Qt::CopyAction;
+ defaultOp = Qt::CopyAction;
+ break;
+ case DragMove:
+ allowedOps = Qt::MoveAction;
+ defaultOp = Qt::MoveAction;
+ break;
+ case DragLink:
+ allowedOps = Qt::LinkAction;
+ defaultOp = Qt::LinkAction;
+ break;
+ }
+ bool retval = (drag->exec(allowedOps, defaultOp) == Qt::MoveAction);
+ last_target = drag->target();
+
+ return retval;
+}
+
+#endif
+
+
+/*!
+ Returns a pointer to the widget where this object originated (the drag
+ source).
+*/
+
+QWidget * Q3DragObject::source()
+{
+ if (parent() && parent()->isWidgetType())
+ return (QWidget *)parent();
+ else
+ return 0;
+}
+
+
+/*!
+ \class Q3DragObject
+
+ \brief The Q3DragObject class encapsulates MIME-based data
+ transfer.
+
+ \compat
+
+ Q3DragObject is the base class for all data that needs to be
+ transferred between and within applications, both for drag and
+ drop and for the clipboard.
+
+ See the \link dnd.html Drag and drop documentation\endlink for an
+ overview of how to provide drag and drop in your application.
+
+ See the QClipboard documentation for an overview of how to provide
+ cut and paste in your application.
+
+ The drag() function is used to start a drag operation. You can
+ specify the \l DragMode in the call or use one of the convenience
+ functions dragCopy(), dragMove(), or dragLink(). The drag source
+ where the data originated is retrieved with source(). If the data
+ was dropped on a widget within the application, target() will
+ return a pointer to that widget. Specify the pixmap to display
+ during the drag with setPixmap().
+*/
+
+static
+void stripws(QByteArray& s)
+{
+ int f;
+ while ((f = s.indexOf(' ')) >= 0)
+ s.remove(f,1);
+}
+
+/*!
+ \class Q3TextDrag
+
+ \brief The Q3TextDrag class is a drag and drop object for
+ transferring plain and Unicode text.
+
+ \compat
+
+ Plain text is passed in a QString which may contain multiple lines
+ (i.e. may contain newline characters). The drag target will receive
+ the newlines according to the runtime environment, e.g. LF on Unix,
+ and CRLF on Windows.
+
+ Qt provides no built-in mechanism for delivering only a single-line.
+
+ For more information about drag and drop, see the Q3DragObject class
+ and the \link dnd.html drag and drop documentation\endlink.
+*/
+
+
+/*!
+ Constructs a text drag object with the given \a name, and sets its data
+ to \a text. The \a dragSource is the widget that the drag operation started
+ from.
+*/
+
+Q3TextDrag::Q3TextDrag(const QString &text, QWidget * dragSource, const char * name)
+ : Q3DragObject(*new Q3TextDragPrivate, dragSource)
+{
+ setObjectName(QLatin1String(name));
+ setText(text);
+}
+
+
+/*!
+ Constructs a default text drag object with the given \a name.
+ The \a dragSource is the widget that the drag operation started from.
+*/
+
+Q3TextDrag::Q3TextDrag(QWidget * dragSource, const char * name)
+ : Q3DragObject(*(new Q3TextDragPrivate), dragSource)
+{
+ setObjectName(QLatin1String(name));
+}
+
+/*! \internal */
+Q3TextDrag::Q3TextDrag(Q3TextDragPrivate &dd, QWidget *dragSource)
+ : Q3DragObject(dd, dragSource)
+{
+
+}
+
+/*!
+ Destroys the text drag object.
+*/
+Q3TextDrag::~Q3TextDrag()
+{
+
+}
+
+/*!
+ \fn void Q3TextDrag::setSubtype(const QString &subtype)
+
+ Sets the MIME \a subtype of the text being dragged. The default subtype
+ is "plain", so the default MIME type of the text is "text/plain".
+ You might use this to declare that the text is "text/html" by calling
+ setSubtype("html").
+*/
+void Q3TextDrag::setSubtype(const QString & st)
+{
+ d_func()->setSubType(st);
+}
+
+/*!
+ Sets the \a text to be dragged. You will need to call this if you did
+ not pass the text during construction.
+*/
+void Q3TextDrag::setText(const QString &text)
+{
+ d_func()->txt = text;
+}
+
+
+/*!
+ \reimp
+*/
+const char * Q3TextDrag::format(int i) const
+{
+ if (i > 0)
+ return 0;
+ return d_func()->fmt.constData();
+}
+
+QTextCodec* qt_findcharset(const QByteArray& mimetype)
+{
+ int i=mimetype.indexOf("charset=");
+ if (i >= 0) {
+ QByteArray cs = mimetype.mid(i+8);
+ stripws(cs);
+ i = cs.indexOf(';');
+ if (i >= 0)
+ cs = cs.left(i);
+ // May return 0 if unknown charset
+ return QTextCodec::codecForName(cs);
+ }
+ // no charset=, use locale
+ return QTextCodec::codecForName("utf-8");
+}
+
+static QTextCodec *codecForHTML(const QByteArray &ba)
+{
+ // determine charset
+ int mib = 0;
+ int pos;
+ QTextCodec *c = 0;
+
+ if (ba.size() > 1 && (((uchar)ba[0] == 0xfe && (uchar)ba[1] == 0xff)
+ || ((uchar)ba[0] == 0xff && (uchar)ba[1] == 0xfe))) {
+ mib = 1015; // utf16
+ } else if (ba.size() > 2
+ && (uchar)ba[0] == 0xef
+ && (uchar)ba[1] == 0xbb
+ && (uchar)ba[2] == 0xbf) {
+ mib = 106; // utf-8
+ } else {
+ pos = 0;
+ while ((pos = ba.indexOf('<', pos)) != -1) {
+ int end = ba.indexOf('>', pos+1);
+ if (end == -1)
+ break;
+ const QString str(QString::fromLatin1(ba.mid(pos, end-pos)));
+ if (str.contains(QLatin1String("meta http-equiv="), Qt::CaseInsensitive)) {
+ pos = str.indexOf(QLatin1String("charset="), 0, Qt::CaseInsensitive) + int(strlen("charset="));
+ if (pos != -1) {
+ int pos2 = ba.indexOf('\"', pos+1);
+ QByteArray cs = ba.mid(pos, pos2-pos);
+ c = QTextCodec::codecForName(cs);
+ if (c)
+ return c;
+ }
+ }
+ pos = end;
+ }
+ }
+ if (mib)
+ c = QTextCodec::codecForMib(mib);
+
+ return c;
+}
+
+static
+QTextCodec* findcodec(const QMimeSource* e)
+{
+ QTextCodec* r = 0;
+ const char* f;
+ int i;
+ for (i=0; (f=e->format(i)); i++) {
+ bool html = !qstrnicmp(f, "text/html", 9);
+ if (html)
+ r = codecForHTML(e->encodedData(f));
+ if (!r)
+ r = qt_findcharset(QByteArray(f).toLower());
+ if (r)
+ return r;
+ }
+ return 0;
+}
+
+
+
+/*!
+ \reimp
+*/
+QByteArray Q3TextDrag::encodedData(const char* mime) const
+{
+ Q_D(const Q3TextDrag);
+ if (mime != d->fmt)
+ return QByteArray();
+ return d->txt.toUtf8();
+}
+
+/*!
+ \fn bool Q3TextDrag::canDecode(const QMimeSource *source)
+
+ Returns true if the information in the MIME \a source can be decoded
+ into a QString; otherwise returns false.
+
+ \sa decode()
+*/
+bool Q3TextDrag::canDecode(const QMimeSource* e)
+{
+ const char* f;
+ for (int i=0; (f=e->format(i)); i++) {
+ if (0==qstrnicmp(f,"text/",5)) {
+ return findcodec(e) != 0;
+ }
+ }
+ return false;
+}
+
+/*!
+ \fn bool Q3TextDrag::decode(const QMimeSource *source, QString &string, QString &subtype)
+
+ \overload
+
+ Attempts to decode the dropped information in the MIME \a source into
+ the \a string given.
+ Returns true if successful; otherwise returns false. If \a subtype
+ is null, any text subtype is accepted; otherwise only the
+ specified \a subtype is accepted.
+
+ \sa canDecode()
+*/
+bool Q3TextDrag::decode(const QMimeSource* e, QString& str, QString& subtype)
+{
+ if(!e)
+ return false;
+
+ const char* mime;
+ for (int i=0; (mime = e->format(i)); i++) {
+ if (0==qstrnicmp(mime,"text/",5)) {
+ QByteArray m(mime);
+ m = m.toLower();
+ int semi = m.indexOf(';');
+ if (semi < 0)
+ semi = m.length();
+ QString foundst(QString::fromLatin1(m.mid(5,semi-5)));
+ if (subtype.isNull() || foundst == subtype) {
+ bool html = !qstrnicmp(mime, "text/html", 9);
+ QTextCodec* codec = 0;
+ if (html)
+ // search for the charset tag in the HTML
+ codec = codecForHTML(e->encodedData(mime));
+ if (!codec)
+ codec = qt_findcharset(m);
+ if (codec) {
+ QByteArray payload;
+
+ payload = e->encodedData(mime);
+ if (payload.size()) {
+ int l;
+ if (codec->mibEnum() != 1015) {
+ // length is at NUL or payload.size()
+ l = 0;
+ while (l < (int)payload.size() && payload[l])
+ l++;
+ } else {
+ l = payload.size();
+ }
+
+ str = codec->toUnicode(payload,l);
+
+ if (subtype.isNull())
+ subtype = foundst;
+
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/*!
+ \fn bool Q3TextDrag::decode(const QMimeSource *source, QString &string)
+
+ Attempts to decode the dropped information in the MIME \a source into
+ the \a string given.
+ Returns true if successful; otherwise returns false.
+
+ \sa canDecode()
+*/
+bool Q3TextDrag::decode(const QMimeSource* e, QString& str)
+{
+ QString st;
+ return decode(e, str, st);
+}
+
+
+/*
+ Q3ImageDrag could use an internal MIME type for communicating QPixmaps
+ and QImages rather than always converting to raw data. This is available
+ for that purpose and others. It is not currently used.
+*/
+
+/*!
+ \class Q3ImageDrag
+
+ \brief The Q3ImageDrag class provides a drag and drop object for
+ transferring images.
+
+ \compat
+
+ Images are offered to the receiving application in multiple
+ formats, determined by Qt's output formats.
+*/
+
+/*!
+ Constructs an image drag object with the given \a name, and sets its
+ data to \a image. The \a dragSource is the widget that the drag operation
+ started from.
+*/
+
+Q3ImageDrag::Q3ImageDrag(QImage image,
+ QWidget * dragSource, const char * name)
+ : Q3DragObject(*(new Q3ImageDragPrivate), dragSource)
+{
+ setObjectName(QLatin1String(name));
+ setImage(image);
+}
+
+/*!
+ Constructs a default image drag object with the given \a name.
+ The \a dragSource is the widget that the drag operation started from.
+*/
+
+Q3ImageDrag::Q3ImageDrag(QWidget * dragSource, const char * name)
+ : Q3DragObject(*(new Q3ImageDragPrivate), dragSource)
+{
+ setObjectName(QLatin1String(name));
+}
+
+/*! \internal */
+Q3ImageDrag::Q3ImageDrag(Q3ImageDragPrivate &dd, QWidget *dragSource)
+ : Q3DragObject(dd, dragSource)
+{
+}
+
+/*!
+ Destroys the image drag object.
+*/
+
+Q3ImageDrag::~Q3ImageDrag()
+{
+ // nothing
+}
+
+
+/*!
+ Sets the \a image to be dragged. You will need to call this if you did
+ not pass the image during construction.
+*/
+void Q3ImageDrag::setImage(QImage image)
+{
+ Q_D(Q3ImageDrag);
+ d->img = image;
+ QList<QByteArray> formats = QImageWriter::supportedImageFormats();
+ formats.removeAll("PBM"); // remove non-raw PPM
+ if (image.depth()!=32) {
+ // BMP better than PPM for paletted images
+ if (formats.removeAll("BMP")) // move to front
+ formats.insert(0,"BMP");
+ }
+ // PNG is best of all
+ if (formats.removeAll("PNG")) // move to front
+ formats.insert(0,"PNG");
+
+ for(int i = 0; i < formats.count(); i++) {
+ QByteArray format("image/");
+ format += formats.at(i);
+ format = format.toLower();
+ if (format == "image/pbmraw")
+ format = "image/ppm";
+ d->ofmts.append(format);
+ }
+ d->ofmts.append("application/x-qt-image");
+}
+
+/*!
+ \reimp
+*/
+const char * Q3ImageDrag::format(int i) const
+{
+ Q_D(const Q3ImageDrag);
+ return i < d->ofmts.count() ? d->ofmts.at(i).data() : 0;
+}
+
+/*!
+ \reimp
+*/
+QByteArray Q3ImageDrag::encodedData(const char* fmt) const
+{
+ Q_D(const Q3ImageDrag);
+ QString imgFormat(fmt);
+ if (imgFormat == QLatin1String("application/x-qt-image"))
+ imgFormat = QLatin1String("image/PNG");
+
+ if (imgFormat.startsWith("image/")){
+ QByteArray f(imgFormat.mid(6).toAscii());
+ QByteArray dat;
+ QBuffer w(&dat);
+ w.open(QIODevice::WriteOnly);
+ QImageWriter writer(&w, f.toUpper());
+ if (!writer.write(d->img))
+ return QByteArray();
+ w.close();
+ return dat;
+ } else {
+ return QByteArray();
+ }
+}
+
+/*!
+ \fn bool Q3ImageDrag::canDecode(const QMimeSource *source)
+
+ Returns true if the information in the MIME \a source can be decoded
+ into an image; otherwise returns false.
+
+ \sa decode()
+*/
+bool Q3ImageDrag::canDecode(const QMimeSource* e)
+{
+ return e->provides("application/x-qt-image");
+}
+
+/*!
+ \fn bool Q3ImageDrag::decode(const QMimeSource *source, QImage &image)
+
+ Decode the dropped information in the MIME \a source into the \a image.
+ Returns true if successful; otherwise returns false.
+
+ \sa canDecode()
+*/
+bool Q3ImageDrag::decode(const QMimeSource* e, QImage& img)
+{
+ if (!e)
+ return false;
+
+ QByteArray payload = e->encodedData("application/x-qt-image");
+ if (payload.isEmpty())
+ return false;
+
+ img.loadFromData(payload);
+ if (img.isNull())
+ return false;
+
+ return true;
+}
+
+/*!
+ \fn bool Q3ImageDrag::decode(const QMimeSource *source, QPixmap &pixmap)
+
+ \overload
+
+ Decodes the dropped information in the MIME \a source into the \a pixmap.
+ Returns true if successful; otherwise returns false.
+
+ This is a convenience function that converts the information to a QPixmap
+ via a QImage.
+
+ \sa canDecode()
+*/
+bool Q3ImageDrag::decode(const QMimeSource* e, QPixmap& pm)
+{
+ if (!e)
+ return false;
+
+ QImage img;
+ // We avoid dither, since the image probably came from this display
+ if (decode(e, img)) {
+ pm = QPixmap::fromImage(img, Qt::AvoidDither);
+ if (pm.isNull())
+ return false;
+
+ return true;
+ }
+ return false;
+}
+
+
+
+
+/*!
+ \class Q3StoredDrag
+ \brief The Q3StoredDrag class provides a simple stored-value drag object for arbitrary MIME data.
+
+ \compat
+
+ When a block of data has only one representation, you can use a
+ Q3StoredDrag to hold it.
+
+ For more information about drag and drop, see the Q3DragObject
+ class and the \link dnd.html drag and drop documentation\endlink.
+*/
+
+/*!
+ Constructs a Q3StoredDrag. The \a dragSource and \a name are passed
+ to the Q3DragObject constructor, and the format is set to \a
+ mimeType.
+
+ The data will be unset. Use setEncodedData() to set it.
+*/
+Q3StoredDrag::Q3StoredDrag(const char* mimeType, QWidget * dragSource, const char * name) :
+ Q3DragObject(*new Q3StoredDragPrivate, dragSource)
+{
+ Q_D(Q3StoredDrag);
+ setObjectName(QLatin1String(name));
+ d->fmt = qstrdup(mimeType);
+}
+
+/*! \internal */
+Q3StoredDrag::Q3StoredDrag(Q3StoredDragPrivate &dd, const char* mimeType, QWidget * dragSource)
+ : Q3DragObject(dd, dragSource)
+{
+ d_func()->fmt = qstrdup(mimeType);
+}
+
+/*!
+ Destroys the drag object.
+*/
+Q3StoredDrag::~Q3StoredDrag()
+{
+ delete [] (char*)d_func()->fmt;
+}
+
+/*!
+ \reimp
+*/
+const char * Q3StoredDrag::format(int i) const
+{
+ if (i==0)
+ return d_func()->fmt;
+ else
+ return 0;
+}
+
+
+/*!
+ \fn void Q3StoredDrag::setEncodedData(const QByteArray &data)
+
+ Sets the encoded \a data of this drag object. The encoded data is
+ delivered to drop sites; it must be in a strictly defined and portable
+ format.
+
+ The drag object can't be dropped (by the user) until this function
+ has been called.
+*/
+
+void Q3StoredDrag::setEncodedData(const QByteArray & encodedData)
+{
+ d_func()->enc = encodedData;
+}
+
+/*!
+ \fn QByteArray Q3StoredDrag::encodedData(const char *format) const
+
+ Returns the stored data in the \a format given.
+
+ \sa setEncodedData()
+*/
+QByteArray Q3StoredDrag::encodedData(const char* m) const
+{
+ if (!qstricmp(m, d_func()->fmt))
+ return d_func()->enc;
+ else
+ return QByteArray();
+}
+
+
+/*!
+ \class Q3UriDrag
+ \brief The Q3UriDrag class provides a drag object for a list of URI references.
+
+ \compat
+
+ URIs are a useful way to refer to files that may be distributed
+ across multiple machines. A URI will often refer to a file on a
+ machine local to both the drag source and the drop target, so the
+ URI can be equivalent to passing a file name but is more
+ extensible.
+
+ Use URIs in Unicode form so that the user can comfortably edit and
+ view them. For use in HTTP or other protocols, use the correctly
+ escaped ASCII form.
+
+ You can convert a list of file names to file URIs using
+ setFileNames(), or into human-readable form with setUnicodeUris().
+
+ Static functions are provided to convert between filenames and
+ URIs; e.g. uriToLocalFile() and localFileToUri(). Static functions
+ are also provided to convert URIs to and from human-readable form;
+ e.g. uriToUnicodeUri() and unicodeUriToUri().
+ You can also decode URIs from a MIME source into a list with
+ decodeLocalFiles() and decodeToUnicodeUris().
+*/
+
+/*!
+ Constructs an object to drag the list of \a uris.
+ The \a dragSource and \a name are passed to the Q3StoredDrag constructor.
+
+ Note that URIs are always in escaped UTF8 encoding.
+*/
+Q3UriDrag::Q3UriDrag(const Q3StrList &uris, QWidget * dragSource, const char * name) :
+ Q3StoredDrag("text/uri-list", dragSource)
+{
+ setObjectName(QLatin1String(name));
+ setUris(uris);
+}
+
+/*!
+ Constructs an object to drag with the given \a name.
+ You must call setUris() before you start the drag().
+ Both the \a dragSource and the \a name are passed to the Q3StoredDrag
+ constructor.
+*/
+Q3UriDrag::Q3UriDrag(QWidget * dragSource, const char * name) :
+ Q3StoredDrag("text/uri-list", dragSource)
+{
+ setObjectName(QLatin1String(name));
+}
+#endif
+
+/*!
+ Destroys the URI drag object.
+*/
+Q3UriDrag::~Q3UriDrag()
+{
+}
+
+/*!
+ \fn void Q3UriDrag::setUris(const QList<QByteArray> &list)
+
+ Changes the \a list of URIs to be dragged.
+
+ Note that URIs are always in escaped UTF8 encoding.
+*/
+void Q3UriDrag::setUris(const QList<QByteArray> &uris)
+{
+ QByteArray a;
+ int c = 0;
+ int i;
+ int count = uris.count();
+ for (i = 0; i < count; ++i)
+ c += uris.at(i).size() + 2; //length + \r\n
+ a.reserve(c+1);
+ for (i = 0; i < count; ++i) {
+ a.append(uris.at(i));
+ a.append("\r\n");
+ }
+ a[c] = 0;
+ setEncodedData(a);
+}
+
+
+/*!
+ \fn bool Q3UriDrag::canDecode(const QMimeSource *source)
+
+ Returns true if decode() can decode the MIME \a source; otherwise
+ returns false.
+*/
+bool Q3UriDrag::canDecode(const QMimeSource* e)
+{
+ return e->provides("text/uri-list");
+}
+
+/*!
+ \fn bool Q3UriDrag::decode(const QMimeSource* source, Q3StrList& list)
+
+ Decodes URIs from the MIME \a source, placing the result in the \a list.
+ The list is cleared before any items are inserted.
+
+ Returns true if the MIME \a source contained a valid list of URIs;
+ otherwise returns false.
+*/
+bool Q3UriDrag::decode(const QMimeSource* e, Q3StrList& l)
+{
+ QByteArray payload = e->encodedData("text/uri-list");
+ if (payload.size()) {
+ l.clear();
+ l.setAutoDelete(true);
+ uint c=0;
+ const char* data = payload.data();
+ while ((int)c < payload.size() && data[c]) {
+ uint f = c;
+ // Find line end
+ while ((int)c < payload.size() && data[c] && data[c]!='\r'
+ && data[c] != '\n')
+ c++;
+ Q3CString s(data+f,c-f+1);
+ if (s[0] != '#') // non-comment?
+ l.append(s);
+ // Skip junk
+ while ((int)c < payload.size() && data[c] &&
+ (data[c]=='\n' || data[c]=='\r'))
+ c++;
+ }
+ return true;
+ }
+ return false;
+}
+
+static uint htod(int h)
+{
+ if (isdigit(h))
+ return h - '0';
+ return tolower(h) - 'a' + 10;
+}
+
+/*!
+ \fn Q3UriDrag::setFilenames(const QStringList &list)
+
+ \obsolete
+
+ Sets the filename's in the drag object to those in the given \a
+ list.
+
+ Use setFileNames() instead.
+*/
+
+/*!
+ \fn void Q3UriDrag::setFileNames(const QStringList &filenames)
+
+ Sets the URIs to be local file URIs equivalent to the \a filenames.
+
+ \sa localFileToUri(), setUris()
+*/
+void Q3UriDrag::setFileNames(const QStringList & fnames)
+{
+ QList<QByteArray> uris;
+ for (QStringList::ConstIterator i = fnames.begin();
+ i != fnames.end(); ++i) {
+ QByteArray fileUri = localFileToUri(*i);
+ if (!fileUri.isEmpty())
+ uris.append(fileUri);
+ }
+
+ setUris(uris);
+}
+
+/*!
+ \fn void Q3UriDrag::setFileNames(const QString &name)
+ \fn void Q3UriDrag::setFilenames(const QString &name)
+
+ Same as setFileNames(QStringList(\a name)).
+*/
+
+/*!
+ \fn void Q3UriDrag::setUnicodeUris(const QStringList &list)
+
+ Sets the URIs in the \a list to be Unicode URIs (only useful for
+ displaying to humans).
+
+ \sa localFileToUri(), setUris()
+*/
+void Q3UriDrag::setUnicodeUris(const QStringList & uuris)
+{
+ QList<QByteArray> uris;
+ for (int i = 0; i < uuris.count(); ++i)
+ uris.append(unicodeUriToUri(uuris.at(i)));
+ setUris(uris);
+}
+
+/*!
+ \fn QByteArray Q3UriDrag::unicodeUriToUri(const QString &string)
+
+ Returns the URI equivalent of the Unicode URI given in the \a string
+ (only useful for displaying to humans).
+
+ \sa uriToLocalFile()
+*/
+QByteArray Q3UriDrag::unicodeUriToUri(const QString& uuri)
+{
+ QByteArray utf8 = uuri.toUtf8();
+ QByteArray escutf8;
+ int n = utf8.length();
+ bool isFile = uuri.startsWith(QLatin1String("file://"));
+ for (int i=0; i<n; i++) {
+ if ((utf8[i] >= 'a' && utf8[i] <= 'z')
+ || utf8[i] == '/'
+ || (utf8[i] >= '0' && utf8[i] <= '9')
+ || (utf8[i] >= 'A' && utf8[i] <= 'Z')
+
+ || utf8[i] == '-' || utf8[i] == '_'
+ || utf8[i] == '.' || utf8[i] == '!'
+ || utf8[i] == '~' || utf8[i] == '*'
+ || utf8[i] == '(' || utf8[i] == ')'
+ || utf8[i] == '\''
+
+ // Allow this through, so that all URI-references work.
+ || (!isFile && utf8[i] == '#')
+
+ || utf8[i] == ';'
+ || utf8[i] == '?' || utf8[i] == ':'
+ || utf8[i] == '@' || utf8[i] == '&'
+ || utf8[i] == '=' || utf8[i] == '+'
+ || utf8[i] == '$' || utf8[i] == ',')
+ {
+ escutf8 += utf8[i];
+ } else {
+ // Everything else is escaped as %HH
+ QString s;
+ s.sprintf("%%%02x",(uchar)utf8[i]);
+ escutf8 += s.latin1();
+ }
+ }
+ return escutf8;
+}
+
+/*!
+ Returns the URI equivalent to the absolute local \a filename.
+
+ \sa uriToLocalFile()
+*/
+QByteArray Q3UriDrag::localFileToUri(const QString& filename)
+{
+ QString r = filename;
+
+ //check that it is an absolute file
+ if (QDir::isRelativePath(r))
+ return QByteArray();
+#ifdef Q_WS_WIN
+
+
+ bool hasHost = false;
+ // convert form network path
+ if (r.left(2) == QLatin1String("\\\\") || r.left(2) == QLatin1String("//")) {
+ r.remove(0, 2);
+ hasHost = true;
+ }
+
+ // Slosh -> Slash
+ int slosh;
+ while ((slosh=r.indexOf(QLatin1Char('\\'))) >= 0) {
+ r[slosh] = QLatin1Char('/');
+ }
+
+ // Drive
+ if (r[0] != QLatin1Char('/') && !hasHost)
+ r.insert(0,QLatin1Char('/'));
+
+#endif
+#if defined (Q_WS_X11) && 0
+ // URL without the hostname is considered to be errorneous by XDnD.
+ // See: http://www.newplanetsoftware.com/xdnd/dragging_files.html
+ // This feature is not active because this would break dnd between old and new qt apps.
+ char hostname[257];
+ if (gethostname(hostname, 255) == 0) {
+ hostname[256] = '\0';
+ r.prepend(QString::fromLatin1(hostname));
+ }
+#endif
+ return unicodeUriToUri(QLatin1String("file://") + r);
+}
+
+/*!
+ \fn QString Q3UriDrag::uriToUnicodeUri(const char *string)
+
+ Returns the Unicode URI (only useful for displaying to humans)
+ equivalent of the URI given in the \a string.
+
+ Note that URIs are always in escaped UTF8 encoding.
+
+ \sa localFileToUri()
+*/
+QString Q3UriDrag::uriToUnicodeUri(const char* uri)
+{
+ QByteArray utf8;
+
+ while (*uri) {
+ switch (*uri) {
+ case '%': {
+ uint ch = (uchar) uri[1];
+ if (ch && uri[2]) {
+ ch = htod(ch) * 16 + htod((uchar) uri[2]);
+ utf8 += (char) ch;
+ uri += 2;
+ }
+ }
+ break;
+ default:
+ utf8 += *uri;
+ }
+ ++uri;
+ }
+
+ return QString::fromUtf8(utf8);
+}
+
+/*!
+ \fn QString Q3UriDrag::uriToLocalFile(const char *string)
+
+ Returns the name of a local file equivalent to the URI given in the
+ \a string, or an empty string if it does not refer to a local file.
+
+ Note that URIs are always in escaped UTF8 encoding.
+
+ \sa localFileToUri()
+*/
+QString Q3UriDrag::uriToLocalFile(const char* uri)
+{
+ QString file;
+
+ if (!uri)
+ return file;
+ if (0==qstrnicmp(uri,"file:/",6)) // It is a local file uri
+ uri += 6;
+ else if (QString::fromLatin1(uri).indexOf(QLatin1String(":/")) != -1) // It is a different scheme uri
+ return file;
+
+ bool local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/');
+#ifdef Q_WS_X11
+ // do we have a hostname?
+ if (!local && uri[0] == '/' && uri[2] != '/') {
+ // then move the pointer to after the 'hostname/' part of the uri
+ const char* hostname_end = strchr(uri+1, '/');
+ if (hostname_end != NULL) {
+ char hostname[257];
+ if (gethostname(hostname, 255) == 0) {
+ hostname[256] = '\0';
+ if (qstrncmp(uri+1, hostname, hostname_end - (uri+1)) == 0) {
+ uri = hostname_end + 1; // point after the slash
+ local = true;
+ }
+ }
+ }
+ }
+#endif
+ if (local) {
+ file = uriToUnicodeUri(uri);
+ if (uri[1] == '/') {
+ file.remove((uint)0,1);
+ } else {
+ file.insert(0, QLatin1Char('/'));
+ }
+#ifdef Q_WS_WIN
+ if (file.length() > 2 && file[0] == QLatin1Char('/') && file[2] == QLatin1Char('|')) {
+ file[2] = QLatin1Char(':');
+ file.remove(0,1);
+ } else if (file.length() > 2 && file[0] == QLatin1Char('/') && file[1].isLetter() && file[2] == QLatin1Char(':')) {
+ file.remove(0, 1);
+ }
+ // Leave slash as slashes.
+#endif
+ }
+#ifdef Q_WS_WIN
+ else {
+ file = uriToUnicodeUri(uri);
+ // convert to network path
+ file.insert(1, QLatin1Char('/')); // leave as forward slashes
+ }
+#endif
+
+ return file;
+}
+
+/*!
+ \fn bool Q3UriDrag::decodeLocalFiles(const QMimeSource *source, QStringList &list)
+
+ Decodes URIs from the MIME \a source, converting them to local filenames
+ where possible, and places them in the \a list (which is first cleared).
+
+ Returns true if the MIME \a source contained a valid list of URIs;
+ otherwise returns false. The list will be empty if no URIs referred to
+ local files.
+*/
+bool Q3UriDrag::decodeLocalFiles(const QMimeSource* e, QStringList& l)
+{
+ Q3StrList u;
+ if (!decode(e, u))
+ return false;
+
+ l.clear();
+ for (uint i = 0; i < u.count(); ++i) {
+ QString lf = uriToLocalFile(u.at(i));
+ if (!lf.isEmpty())
+ l.append(lf);
+ }
+ return true;
+}
+
+/*!
+ \fn bool Q3UriDrag::decodeToUnicodeUris(const QMimeSource *source, QStringList &list)
+
+ Decodes URIs from the MIME \a source, converting them to Unicode URIs
+ (only useful for displaying to humans), and places them in the \a list
+ (which is first cleared).
+
+ Returns true if the MIME \a source contained a valid list of URIs;
+ otherwise returns false.
+*/
+bool Q3UriDrag::decodeToUnicodeUris(const QMimeSource* e, QStringList& l)
+{
+ Q3StrList u;
+ if (!decode(e, u))
+ return false;
+
+ l.clear();
+ for (uint i = 0; i < u.count(); ++i)
+ l.append(uriToUnicodeUri(u.at(i)));
+
+ return true;
+}
+
+/*!
+ \class Q3ColorDrag
+
+ \brief The Q3ColorDrag class provides a drag and drop object for
+ transferring colors between widgets.
+
+ \compat
+
+ This class provides a drag object which can be used to transfer data
+ about colors for drag and drop and in the clipboard. For example, it
+ is used in QColorDialog.
+
+ The color is set in the constructor but can be changed with
+ setColor().
+
+ For more information about drag and drop, see the Q3DragObject class
+ and the \link dnd.html drag and drop documentation\endlink.
+*/
+
+/*!
+ Constructs a color drag object with the given \a col. Passes \a
+ dragsource and \a name to the Q3StoredDrag constructor.
+*/
+
+Q3ColorDrag::Q3ColorDrag(const QColor &col, QWidget *dragsource, const char *name)
+ : Q3StoredDrag("application/x-color", dragsource)
+{
+ setObjectName(QLatin1String(name));
+ setColor(col);
+}
+
+/*!
+ Constructs a color drag object with a white color. Passes \a
+ dragsource and \a name to the Q3StoredDrag constructor.
+*/
+
+Q3ColorDrag::Q3ColorDrag(QWidget *dragsource, const char *name)
+ : Q3StoredDrag("application/x-color", dragsource)
+{
+ setObjectName(QLatin1String(name));
+ setColor(Qt::white);
+}
+
+/*!
+ \fn void Q3ColorDrag::setColor(const QColor &color)
+
+ Sets the \a color of the color drag.
+*/
+
+void Q3ColorDrag::setColor(const QColor &col)
+{
+ short r = (col.red() << 8) | col.red();
+ short g = (col.green() << 8) | col.green();
+ short b = (col.blue() << 8) | col.blue();
+
+ // make sure we transmit data in network order
+ r = htons(r);
+ g = htons(g);
+ b = htons(b);
+
+ ushort rgba[4] = {
+ r, g, b,
+ 0xffff // Alpha not supported yet.
+ };
+ QByteArray data;
+ data.resize(sizeof(rgba));
+ memcpy(data.data(), rgba, sizeof(rgba));
+ setEncodedData(data);
+}
+
+/*!
+ \fn bool Q3ColorDrag::canDecode(QMimeSource *source)
+
+ Returns true if the color drag object can decode the MIME \a source;
+ otherwise returns false.
+*/
+
+bool Q3ColorDrag::canDecode(QMimeSource *e)
+{
+ return e->provides("application/x-color");
+}
+
+/*!
+ \fn bool Q3ColorDrag::decode(QMimeSource *source, QColor &color)
+
+ Decodes the MIME \a source, and sets the decoded values to the
+ given \a color. Returns true if the decoding is successful.
+ Returns false if the size of the encoded data is not the
+ expected size.
+*/
+
+bool Q3ColorDrag::decode(QMimeSource *e, QColor &col)
+{
+ QByteArray data = e->encodedData("application/x-color");
+ ushort rgba[4];
+ if (data.size() != sizeof(rgba))
+ return false;
+
+ memcpy(rgba, data.constData(), sizeof(rgba));
+
+ short r = rgba[0];
+ short g = rgba[1];
+ short b = rgba[2];
+ short a = rgba[3];
+
+ // data is in network order
+ r = ntohs(r);
+ g = ntohs(g);
+ b = ntohs(b);
+ a = ntohs(a);
+
+ r = (r >> 8) & 0xff;
+ g = (g >> 8) & 0xff;
+ b = (b >> 8) & 0xff;
+ a = (a >> 8) & 0xff;
+
+ col.setRgb(r, g, b, a);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/other/q3dragobject.h b/src/qt3support/other/q3dragobject.h
new file mode 100644
index 0000000..cf708e5
--- /dev/null
+++ b/src/qt3support/other/q3dragobject.h
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DRAGOBJECT_H
+#define Q3DRAGOBJECT_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qmime.h>
+#include <QtGui/qimage.h>
+#include <Qt3Support/q3strlist.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QWidget;
+class Q3TextDragPrivate;
+class Q3DragObjectPrivate;
+class Q3StoredDragPrivate;
+class Q3ImageDragPrivate;
+class Q3ImageDrag;
+class Q3TextDrag;
+class Q3StrList;
+class QImage;
+class QPixmap;
+
+class Q_COMPAT_EXPORT Q3DragObject : public QObject, public QMimeSource {
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(Q3DragObject)
+public:
+ Q3DragObject(QWidget * dragSource = 0, const char *name = 0);
+ virtual ~Q3DragObject();
+
+ bool drag();
+ bool dragMove();
+ void dragCopy();
+ void dragLink();
+
+ virtual void setPixmap(QPixmap);
+ virtual void setPixmap(QPixmap, const QPoint& hotspot);
+ QPixmap pixmap() const;
+ QPoint pixmapHotSpot() const;
+
+ QWidget * source();
+ static QWidget * target();
+
+ enum DragMode { DragDefault, DragCopy, DragMove, DragLink, DragCopyOrMove };
+
+protected:
+ Q3DragObject(Q3DragObjectPrivate &, QWidget *dragSource = 0);
+ virtual bool drag(DragMode);
+
+private:
+ friend class QDragMime;
+ Q_DISABLE_COPY(Q3DragObject)
+};
+
+class Q_COMPAT_EXPORT Q3StoredDrag: public Q3DragObject {
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(Q3StoredDrag)
+public:
+ Q3StoredDrag(const char *mimeType, QWidget *dragSource = 0, const char *name = 0);
+ ~Q3StoredDrag();
+
+ virtual void setEncodedData(const QByteArray &);
+
+ const char * format(int i) const;
+ virtual QByteArray encodedData(const char*) const;
+
+protected:
+ Q3StoredDrag(Q3StoredDragPrivate &, const char *mimeType, QWidget *dragSource = 0);
+
+private:
+ Q_DISABLE_COPY(Q3StoredDrag)
+};
+
+class Q_COMPAT_EXPORT Q3TextDrag: public Q3DragObject {
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(Q3TextDrag)
+public:
+ Q3TextDrag(const QString &, QWidget *dragSource = 0, const char *name = 0);
+ Q3TextDrag(QWidget * dragSource = 0, const char * name = 0);
+ ~Q3TextDrag();
+
+ virtual void setText(const QString &);
+ virtual void setSubtype(const QString &);
+
+ const char * format(int i) const;
+ virtual QByteArray encodedData(const char*) const;
+
+ static bool canDecode(const QMimeSource* e);
+ static bool decode(const QMimeSource* e, QString& s);
+ static bool decode(const QMimeSource* e, QString& s, QString& subtype);
+
+protected:
+ Q3TextDrag(Q3TextDragPrivate &, QWidget * dragSource = 0);
+
+private:
+ Q_DISABLE_COPY(Q3TextDrag)
+};
+
+class Q_COMPAT_EXPORT Q3ImageDrag: public Q3DragObject {
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(Q3ImageDrag)
+public:
+ Q3ImageDrag(QImage image, QWidget * dragSource = 0, const char * name = 0);
+ Q3ImageDrag(QWidget * dragSource = 0, const char * name = 0);
+ ~Q3ImageDrag();
+
+ virtual void setImage(QImage image);
+
+ const char * format(int i) const;
+ virtual QByteArray encodedData(const char*) const;
+
+ static bool canDecode(const QMimeSource* e);
+ static bool decode(const QMimeSource* e, QImage& i);
+ static bool decode(const QMimeSource* e, QPixmap& i);
+
+protected:
+ Q3ImageDrag(Q3ImageDragPrivate &, QWidget * dragSource = 0);
+
+private:
+ Q_DISABLE_COPY(Q3ImageDrag)
+};
+
+
+class Q_COMPAT_EXPORT Q3UriDrag: public Q3StoredDrag {
+ Q_OBJECT
+
+public:
+ Q3UriDrag(const Q3StrList &uris, QWidget * dragSource = 0, const char * name = 0);
+ Q3UriDrag(QWidget * dragSource = 0, const char * name = 0);
+ ~Q3UriDrag();
+
+ void setFileNames(const QStringList & fnames);
+ inline void setFileNames(const QString & fname) { setFileNames(QStringList(fname)); }
+ void setFilenames(const QStringList & fnames) { setFileNames(fnames); }
+ inline void setFilenames(const QString & fname) { setFileNames(QStringList(fname)); }
+ void setUnicodeUris(const QStringList & uuris);
+ virtual void setUris(const QList<QByteArray> &uris);
+
+ static QString uriToLocalFile(const char*);
+ static QByteArray localFileToUri(const QString&);
+ static QString uriToUnicodeUri(const char*);
+ static QByteArray unicodeUriToUri(const QString&);
+ static bool canDecode(const QMimeSource* e);
+ static bool decode(const QMimeSource* e, Q3StrList& i);
+ static bool decodeToUnicodeUris(const QMimeSource* e, QStringList& i);
+ static bool decodeLocalFiles(const QMimeSource* e, QStringList& i);
+
+private:
+ Q_DISABLE_COPY(Q3UriDrag)
+};
+
+class Q_COMPAT_EXPORT Q3ColorDrag : public Q3StoredDrag
+{
+ Q_OBJECT
+ QColor color;
+
+public:
+ Q3ColorDrag(const QColor &col, QWidget *dragsource = 0, const char *name = 0);
+ Q3ColorDrag(QWidget * dragSource = 0, const char * name = 0);
+ void setColor(const QColor &col);
+
+ static bool canDecode(QMimeSource *);
+ static bool decode(QMimeSource *, QColor &col);
+
+private:
+ Q_DISABLE_COPY(Q3ColorDrag)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DRAGOBJECT_H
diff --git a/src/qt3support/other/q3dropsite.cpp b/src/qt3support/other/q3dropsite.cpp
new file mode 100644
index 0000000..3a79d8c
--- /dev/null
+++ b/src/qt3support/other/q3dropsite.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dropsite.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qwidget.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3DropSite
+ \brief The Q3DropSite class provides nothing and does nothing.
+
+ \compat
+
+ It was used in Qt 1.x to do some drag and drop; that has since been
+ folded into QWidget.
+
+ \sa Q3DragObject, Q3TextDrag, Q3ImageDrag
+*/
+
+/*!
+ Constructs a Q3DropSite to handle events for the widget \a self.
+
+ Pass \c this as the \a self parameter.
+ This enables dropping by calling QWidget::setAcceptDrops(true).
+*/
+Q3DropSite::Q3DropSite(QWidget* self)
+{
+ self->setAcceptDrops(true);
+}
+
+/*!
+ Destroys the drop site.
+*/
+Q3DropSite::~Q3DropSite()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/qt3support/other/q3dropsite.h b/src/qt3support/other/q3dropsite.h
new file mode 100644
index 0000000..9205c4e
--- /dev/null
+++ b/src/qt3support/other/q3dropsite.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DROPSITE_H
+#define Q3DROPSITE_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QWidget;
+
+class Q_COMPAT_EXPORT Q3DropSite {
+public:
+ Q3DropSite(QWidget* parent);
+ virtual ~Q3DropSite();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDROPSITE_H
diff --git a/src/qt3support/other/q3gridlayout.h b/src/qt3support/other/q3gridlayout.h
new file mode 100644
index 0000000..e9cd7cd
--- /dev/null
+++ b/src/qt3support/other/q3gridlayout.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GRIDLAYOUT_H
+#define Q3GRIDLAYOUT_H
+
+#include <QtGui/qboxlayout.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3GridLayout : public QGridLayout
+{
+public:
+ inline explicit Q3GridLayout(QWidget *parent)
+ : QGridLayout(parent) { setMargin(0); setSpacing(0); }
+
+ inline Q3GridLayout(QWidget *parent, int nRows, int nCols = 1, int margin = 0,
+ int spacing = -1, const char *name = 0)
+ : QGridLayout(parent, nRows, nCols, margin, spacing, name) {}
+
+ inline Q3GridLayout(int nRows, int nCols = 1, int spacing = -1, const char *name = 0)
+ : QGridLayout(nRows, nCols, spacing, name) {}
+
+ inline Q3GridLayout(QLayout *parentLayout, int nRows =1, int nCols = 1, int spacing = -1,
+ const char *name = 0)
+ : QGridLayout(parentLayout, nRows, nCols, spacing, name) {}
+
+private:
+ Q_DISABLE_COPY(Q3GridLayout)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GRIDLAYOUT_H
diff --git a/src/qt3support/other/q3membuf.cpp b/src/qt3support/other/q3membuf.cpp
new file mode 100644
index 0000000..7e45eb9
--- /dev/null
+++ b/src/qt3support/other/q3membuf.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3membuf_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// *******************************************************************
+// QMembuf declaration and implementation
+// *******************************************************************
+
+/* \internal
+ This class implements an efficient buffering of data that is often used by
+ asynchronous IO classes like QSocket, QHttp and QProcess.
+*/
+
+Q3Membuf::Q3Membuf() : _size(0), _index(0)
+{
+}
+
+Q3Membuf::~Q3Membuf()
+{
+ while (!buf.isEmpty())
+ delete buf.takeFirst();
+}
+
+/*! \internal
+ This function consumes \a nbytes bytes of data from the
+ buffer and copies it into \a sink. If \a sink is a 0 pointer
+ the data goes into the nirvana.
+*/
+bool Q3Membuf::consumeBytes(Q_ULONG nbytes, char *sink)
+{
+ if (nbytes <= 0 || (qint64)nbytes > _size)
+ return false;
+ _size -= nbytes;
+ while (!buf.isEmpty()) {
+ QByteArray *a = buf.first();
+ if ((int)(_index + nbytes) >= a->size()) {
+ // Here we skip the whole byte array and get the next later
+ int len = a->size() - _index;
+ if (sink) {
+ memcpy(sink, a->constData()+_index, len);
+ sink += len;
+ }
+ nbytes -= len;
+ buf.removeFirst();
+ delete a;
+ _index = 0;
+ if (nbytes == 0)
+ break;
+ } else {
+ // Here we skip only a part of the first byte array
+ if (sink)
+ memcpy(sink, a->constData()+_index, nbytes);
+ _index += nbytes;
+ break;
+ }
+ }
+ return true;
+}
+
+/*! \internal
+ Scans for any occurrence of '\n' in the buffer. If \a store
+ is not 0 the text up to the first '\n' (or terminating 0) is
+ written to \a store, and a terminating 0 is appended to \a store
+ if necessary. Returns true if a '\n' was found; otherwise returns
+ false.
+*/
+bool Q3Membuf::scanNewline(QByteArray *store)
+{
+ if (_size == 0)
+ return false;
+ int i = 0; // index into 'store'
+ QByteArray *a = 0;
+ char *p;
+ int n;
+ bool retval = false;
+ for (int j = 0; j < buf.size(); ++j) {
+ a = buf.at(j);
+ p = a->data();
+ n = a->size();
+ if (!j) {
+ // first buffer
+ p += _index;
+ n -= _index;
+ }
+ if (store) {
+ while (n-- > 0) {
+ *(store->data()+i) = *p;
+ if (++i == (int)store->size())
+ store->resize(store->size() < 256
+ ? 1024 : store->size()*4);
+ if (*p == '\n') {
+ retval = true;
+ goto end;
+ }
+ p++;
+ }
+ } else {
+ while (n-- > 0) {
+ if(*p == '\n')
+ return true;
+ p++;
+ }
+ }
+ }
+ end:
+ if (store)
+ store->resize(i);
+ return retval;
+}
+
+int Q3Membuf::ungetch(int ch)
+{
+ if (buf.isEmpty() || _index==0) {
+ // we need a new QByteArray
+ QByteArray *ba = new QByteArray;
+ ba->resize(1);
+ buf.prepend(ba);
+ _size++;
+ (*ba)[0] = ch;
+ } else {
+ // we can reuse a place in the buffer
+ QByteArray *ba = buf.first();
+ _index--;
+ _size++;
+ (*ba)[(int)_index] = ch;
+ }
+ return ch;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/other/q3membuf_p.h b/src/qt3support/other/q3membuf_p.h
new file mode 100644
index 0000000..1e5c28e
--- /dev/null
+++ b/src/qt3support/other/q3membuf_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3MEMBUF_P_H
+#define Q3MEMBUF_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qbytearray.h"
+#include "QtCore/qlist.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_COMPAT_EXPORT Q3Membuf
+{
+public:
+ Q3Membuf();
+ ~Q3Membuf();
+
+ void append(QByteArray *ba);
+ void clear();
+
+ bool consumeBytes(Q_ULONG nbytes, char *sink);
+ QByteArray readAll();
+ bool scanNewline(QByteArray *store);
+ bool canReadLine() const;
+
+ int ungetch(int ch);
+
+ qint64 size() const;
+
+private:
+
+ QList<QByteArray *> buf;
+ qint64 _size;
+ qint64 _index;
+};
+
+inline void Q3Membuf::append(QByteArray *ba)
+{ buf.append(ba); _size += ba->size(); }
+
+inline void Q3Membuf::clear()
+{ qDeleteAll(buf); buf.clear(); _size=0; _index=0; }
+
+inline QByteArray Q3Membuf::readAll()
+{ QByteArray ba; ba.resize(_size); consumeBytes(_size,ba.data()); return ba; }
+
+inline bool Q3Membuf::canReadLine() const
+{ return const_cast<Q3Membuf*>(this)->scanNewline(0); }
+
+inline qint64 Q3Membuf::size() const
+{ return _size; }
+
+QT_END_NAMESPACE
+
+#endif // Q3MEMBUF_P_H
diff --git a/src/qt3support/other/q3mimefactory.cpp b/src/qt3support/other/q3mimefactory.cpp
new file mode 100644
index 0000000..de0f1a8
--- /dev/null
+++ b/src/qt3support/other/q3mimefactory.cpp
@@ -0,0 +1,546 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3mimefactory.h"
+
+#ifndef QT_NO_MIMEFACTORY
+
+#include "qmap.h"
+#include "qmime.h"
+#include "qstringlist.h"
+#include "qfileinfo.h"
+#include "qdir.h"
+#include "q3dragobject.h"
+#include "qpixmap.h"
+#include "qimagereader.h"
+#include "q3cleanuphandler.h"
+#include "private/qtextimagehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static Q3MimeSourceFactory* defaultfactory = 0;
+static Q3SingleCleanupHandler<Q3MimeSourceFactory> qmime_cleanup_factory;
+
+class Q3MimeSourceFactoryData {
+public:
+ Q3MimeSourceFactoryData() :
+ last(0)
+ {
+ }
+
+ ~Q3MimeSourceFactoryData()
+ {
+ QMap<QString, QMimeSource*>::Iterator it = stored.begin();
+ while (it != stored.end()) {
+ delete *it;
+ ++it;
+ }
+ delete last;
+ }
+
+ QMap<QString, QMimeSource*> stored;
+ QMap<QString, QString> extensions;
+ QStringList path;
+ QMimeSource* last;
+ QList<Q3MimeSourceFactory*> factories;
+};
+
+static QImage richTextImageLoader(const QString &name, const QString &context)
+{
+ QImage img;
+
+ const QMimeSource *src = Q3MimeSourceFactory::defaultFactory()->data(name, context);
+ if (src && Q3ImageDrag::decode(src, img))
+ return img;
+
+ return QImage();
+}
+
+/*!
+ \class Q3MimeSourceFactory
+ \brief The Q3MimeSourceFactory class is an extensible provider of mime-typed data.
+
+ \compat
+
+ A Q3MimeSourceFactory provides an abstract interface to a
+ collection of information. Each piece of information is
+ represented by a QMimeSource object which can be examined and
+ converted to concrete data types by functions such as
+ Q3ImageDrag::canDecode() and Q3ImageDrag::decode().
+
+ The base Q3MimeSourceFactory can be used in two ways: as an
+ abstraction of a collection of files or as specifically stored
+ data. For it to access files, call setFilePath() before accessing
+ data. For stored data, call setData() for each item (there are
+ also convenience functions, e.g. setText(), setImage() and
+ setPixmap(), that simply call setData() with appropriate
+ parameters).
+
+ The rich text widgets, QTextEdit and QTextBrowser, use
+ Q3MimeSourceFactory to resolve references such as images or links
+ within rich text documents. They either access the default factory
+ (see \l{defaultFactory()}) or their own. Other classes that are
+ capable of displaying rich text (such as QLabel, QWhatsThis or
+ QMessageBox) always use the default factory.
+
+ A factory can also be used as a container to store data associated
+ with a name. This technique is useful whenever rich text contains
+ images that are stored in the program itself, not loaded from the
+ hard disk. Your program may, for example, define some image data
+ as:
+ \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 0
+
+ To be able to use this image within some rich text, for example
+ inside a QLabel, you must create a QImage from the raw data and
+ insert it into the factory with a unique name:
+ \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 1
+
+ Now you can create a rich text QLabel with
+
+ \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 2
+
+ When no longer needed, you can clear the data from the factory:
+
+ \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 3
+*/
+
+
+/*!
+ Constructs a Q3MimeSourceFactory that has no file path and no
+ stored content.
+*/
+Q3MimeSourceFactory::Q3MimeSourceFactory() :
+ d(new Q3MimeSourceFactoryData)
+{
+ addFilePath(QLatin1String(":/qt/q3mimesourcefactory/")); //to get from the resources
+ // add some reasonable defaults
+ setExtensionType(QLatin1String("htm"), "text/html;charset=iso8859-1");
+ setExtensionType(QLatin1String("html"), "text/html;charset=iso8859-1");
+ setExtensionType(QLatin1String("txt"), "text/plain");
+ setExtensionType(QLatin1String("xml"), "text/xml;charset=UTF-8");
+ setExtensionType(QLatin1String("jpg"), "image/jpeg"); // support misspelled jpeg files
+}
+
+/*!
+ Destroys the Q3MimeSourceFactory, deleting all stored content.
+*/
+Q3MimeSourceFactory::~Q3MimeSourceFactory()
+{
+ if (defaultFactory() == this)
+ defaultfactory = 0;
+ delete d;
+}
+
+QMimeSource* Q3MimeSourceFactory::dataInternal(const QString& abs_name, const QMap<QString, QString> &extensions) const
+{
+ QMimeSource* r = 0;
+ QStringList attempted_names(abs_name);
+ QFileInfo fi(abs_name);
+ if (fi.isReadable()) {
+ // get the right mimetype
+ QString e = fi.extension(false);
+ QByteArray mimetype("application/octet-stream");
+ if (extensions.contains(e))
+ mimetype = extensions[e].latin1();
+ if (!QImageReader::imageFormat(abs_name).isEmpty())
+ mimetype = "application/x-qt-image";
+
+ QFile f(abs_name);
+ if (f.open(QIODevice::ReadOnly) && f.size()) {
+ QByteArray ba;
+ ba.resize(f.size());
+ f.readBlock(ba.data(), ba.size());
+ Q3StoredDrag* sr = new Q3StoredDrag(mimetype);
+ sr->setEncodedData(ba);
+ delete d->last;
+ d->last = r = sr;
+ }
+ }
+
+ // we didn't find the mime-source, so ask the default factory for
+ // the mime-source (this one will iterate over all installed ones)
+ //
+ // this looks dangerous, as this dataInternal() function will be
+ // called again when the default factory loops over all installed
+ // factories (including this), but the static bool looping in
+ // data() avoids endless recursions
+ if (!r && this != defaultFactory())
+ r = (QMimeSource*)defaultFactory()->data(abs_name);
+
+ return r;
+}
+
+
+/*!
+ Returns a reference to the data associated with \a abs_name. The
+ return value remains valid only until the next data() or setData()
+ call, so you should immediately decode the result.
+
+ If there is no data associated with \a abs_name in the factory's
+ store, the factory tries to access the local filesystem. If \a
+ abs_name isn't an absolute file name, the factory will search for
+ it in all defined paths (see \l{setFilePath()}).
+
+ The factory understands all the image formats supported by
+ QImageReader. Any other mime types are determined by the file name
+ extension. The default settings are
+ \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 4
+ The effect of these is that file names ending in "txt" will be
+ treated as text encoded in the local encoding; those ending in
+ "xml" will be treated as text encoded in Unicode UTF-8 encoding.
+ The text/html type is treated specially, since the encoding can be
+ specified in the html file itself. "html" or "htm" will be treated
+ as text encoded in the encoding specified by the html meta tag, if
+ none could be found, the charset of the mime type will be used.
+ The text subtype ("html", "plain", or "xml") does not affect the
+ factory, but users of the factory may behave differently. We
+ recommend creating "xml" files where practical. These files can be
+ viewed regardless of the runtime encoding and can encode any
+ Unicode characters without resorting to encoding definitions
+ inside the file.
+
+ Any file data that is not recognized will be retrieved as a
+ QMimeSource providing the "application/octet-stream" mime type,
+ meaning uninterpreted binary data.
+
+ You can add further extensions or change existing ones with
+ subsequent calls to setExtensionType(). If the extension mechanism
+ is not sufficient for your problem domain, you can inherit
+ Q3MimeSourceFactory and reimplement this function to perform some
+ more specialized mime-type detection. The same applies if you want
+ to use the mime source factory to access URL referenced data over
+ a network.
+*/
+const QMimeSource *Q3MimeSourceFactory::data(const QString& abs_name) const
+{
+ if (d->stored.contains(abs_name))
+ return d->stored[abs_name];
+
+ const QMimeSource *r = 0;
+ if (abs_name.isEmpty())
+ return r;
+ QStringList::Iterator it;
+ if (abs_name[0] == QLatin1Char('/')
+#ifdef Q_WS_WIN
+ || (abs_name[0].isLetter() && abs_name[1] == QLatin1Char(':')) || abs_name.startsWith(QLatin1String("\\\\"))
+#endif
+ )
+ {
+ // handle absolute file names directly
+ r = dataInternal(abs_name, d->extensions);
+ }
+ else { // check list of paths
+ for (it = d->path.begin(); !r && it != d->path.end(); ++it) {
+ QString filename = *it;
+ if (filename[(int)filename.length()-1] != QLatin1Char('/'))
+ filename += QLatin1Char('/');
+ filename += abs_name;
+ r = dataInternal(filename, d->extensions);
+ }
+ }
+
+ static bool looping = false;
+ if (!r && this == defaultFactory()) {
+ // we found no mime-source and we are the default factory, so
+ // we know all the other installed mime-source factories, so
+ // ask them
+ if (!looping) {
+ // to avoid endless recustions, don't enter the loop below
+ // if data() got called from within the loop below
+ looping = true;
+ for (int i = 0; i < d->factories.size(); ++i) {
+ const Q3MimeSourceFactory *f = d->factories.at(i);
+ if (f == this)
+ continue;
+ r = static_cast<const QMimeSource *>(f->data(abs_name));
+ if (r) {
+ looping = false;
+ return r;
+ }
+ }
+ looping = false;
+ }
+ } else if (!r) {
+ // we are not the default mime-source factory, so ask the
+ // default one for the mime-source, as this one will loop over
+ // all installed mime-source factories and ask these
+ r = static_cast<const QMimeSource *>(defaultFactory()->data(abs_name));
+ }
+ return r;
+}
+
+/*!
+ \fn void Q3MimeSourceFactory::setFilePath(const QStringList &path)
+ \fn void Q3MimeSourceFactory::setFilePath(const QString &path)
+
+ Sets the list of directories that will be searched when named data
+ is requested to those given in the string list \a path.
+
+ \sa filePath()
+*/
+void Q3MimeSourceFactory::setFilePath(const QStringList& path)
+{
+ d->path = path;
+}
+
+/*!
+ Returns the currently set search paths.
+*/
+QStringList Q3MimeSourceFactory::filePath() const
+{
+ return d->path;
+}
+
+/*!
+ Adds another search path, \a p to the existing search paths.
+
+ \sa setFilePath()
+*/
+void Q3MimeSourceFactory::addFilePath(const QString& p)
+{
+ d->path += p;
+}
+
+/*!
+ Sets the mime-type to be associated with the file name extension,
+ \a ext to \a mimetype. This determines the mime-type for files
+ found via the paths set by setFilePath().
+*/
+void Q3MimeSourceFactory::setExtensionType(const QString& ext, const char* mimetype)
+{
+ d->extensions.insert(ext, QLatin1String(mimetype));
+}
+
+/*!
+ Converts the absolute or relative data item name \a
+ abs_or_rel_name to an absolute name, interpreted within the
+ context (path) of the data item named \a context (this must be an
+ absolute name).
+*/
+QString Q3MimeSourceFactory::makeAbsolute(const QString& abs_or_rel_name, const QString& context) const
+{
+ if (context.isNull() ||
+ !(context[0] == QLatin1Char('/')
+#ifdef Q_WS_WIN
+ || (context[0].isLetter() && context[1] == QLatin1Char(':'))
+#endif
+ ))
+ return abs_or_rel_name;
+ if (abs_or_rel_name.isEmpty())
+ return context;
+ QFileInfo c(context);
+ if (!c.isDir()) {
+ QFileInfo r(c.dir(true), abs_or_rel_name);
+ return r.absFilePath();
+ } else {
+ QDir d(context);
+ QFileInfo r(d, abs_or_rel_name);
+ return r.absFilePath();
+ }
+}
+
+/*!
+ \overload
+ A convenience function. See data(const QString& abs_name). The
+ file name is given in \a abs_or_rel_name and the path is in \a
+ context.
+*/
+const QMimeSource* Q3MimeSourceFactory::data(const QString& abs_or_rel_name, const QString& context) const
+{
+ const QMimeSource* r = data(makeAbsolute(abs_or_rel_name,context));
+ if (!r && !d->path.isEmpty())
+ r = data(abs_or_rel_name);
+ return r;
+}
+
+
+/*!
+ Sets \a text to be the data item associated with the absolute name
+ \a abs_name.
+
+ Equivalent to setData(abs_name, new Q3TextDrag(text)).
+*/
+void Q3MimeSourceFactory::setText(const QString& abs_name, const QString& text)
+{
+ setData(abs_name, new Q3TextDrag(text));
+}
+
+/*!
+ Sets \a image to be the data item associated with the absolute
+ name \a abs_name.
+
+ Equivalent to setData(abs_name, new Q3ImageDrag(image)).
+*/
+void Q3MimeSourceFactory::setImage(const QString& abs_name, const QImage& image)
+{
+ setData(abs_name, new Q3ImageDrag(image));
+}
+
+/*!
+ Sets \a pixmap to be the data item associated with the absolute
+ name \a abs_name.
+*/
+void Q3MimeSourceFactory::setPixmap(const QString& abs_name, const QPixmap& pixmap)
+{
+ setData(abs_name, new Q3ImageDrag(pixmap.convertToImage()));
+}
+
+/*!
+ Sets \a data to be the data item associated with
+ the absolute name \a abs_name. Note that the ownership of \a data is
+ transferred to the factory: do not delete or access the pointer after
+ passing it to this function.
+
+ Passing 0 for data removes previously stored data.
+*/
+void Q3MimeSourceFactory::setData(const QString& abs_name, QMimeSource* data)
+{
+ if (d->stored.contains(abs_name))
+ delete d->stored[abs_name];
+ d->stored.insert(abs_name,data);
+}
+
+
+/*!
+ Returns the application-wide default mime source factory. This
+ factory is used by rich text rendering classes such as
+ QSimpleRichText, QWhatsThis and QMessageBox to resolve named
+ references within rich text documents. It serves also as the
+ initial factory for the more complex render widgets, QTextEdit and
+ QTextBrowser.
+
+ \sa setDefaultFactory()
+*/
+Q3MimeSourceFactory* Q3MimeSourceFactory::defaultFactory()
+{
+ if (!defaultfactory)
+ {
+ defaultfactory = new Q3MimeSourceFactory();
+ qmime_cleanup_factory.set(&defaultfactory);
+ QTextImageHandler::externalLoader = richTextImageLoader;
+ }
+ return defaultfactory;
+}
+
+/*!
+ Sets the default \a factory, destroying any previously set mime
+ source provider. The ownership of the factory is transferred to
+ Qt.
+
+ \sa defaultFactory()
+*/
+void Q3MimeSourceFactory::setDefaultFactory(Q3MimeSourceFactory* factory)
+{
+ if (!defaultfactory)
+ qmime_cleanup_factory.set(&defaultfactory);
+ else if (defaultfactory != factory)
+ delete defaultfactory;
+ defaultfactory = factory;
+}
+
+/*!
+ Sets the defaultFactory() to 0 and returns the previous one.
+*/
+
+Q3MimeSourceFactory* Q3MimeSourceFactory::takeDefaultFactory()
+{
+ Q3MimeSourceFactory *f = defaultfactory;
+ defaultfactory = 0;
+ return f;
+}
+
+/*!
+ Adds the Q3MimeSourceFactory \a f to the list of available
+ mimesource factories. If the defaultFactory() can't resolve a
+ data() it iterates over the list of installed mimesource factories
+ until the data can be resolved.
+
+ \sa removeFactory()
+*/
+
+void Q3MimeSourceFactory::addFactory(Q3MimeSourceFactory *f)
+{
+ Q3MimeSourceFactory::defaultFactory()->d->factories.append(f);
+}
+
+/*!
+ Removes the mimesource factory \a f from the list of available
+ mimesource factories.
+
+ \sa addFactory()
+*/
+
+void Q3MimeSourceFactory::removeFactory(Q3MimeSourceFactory *f)
+{
+ Q3MimeSourceFactory::defaultFactory()->d->factories.removeAll(f);
+}
+
+QPixmap qPixmapFromMimeSource(const QString &abs_name)
+{
+ const QMimeSource *m = Q3MimeSourceFactory::defaultFactory()->data(abs_name);
+ if (!m) {
+ if (QFile::exists(abs_name))
+ return QPixmap(abs_name);
+ if (!abs_name.isEmpty())
+ qWarning("QPixmap::fromMimeSource: Cannot find pixmap \"%s\" in the mime source factory",
+ abs_name.latin1());
+ return QPixmap();
+ }
+ QPixmap pix;
+ Q3ImageDrag::decode(m, pix);
+ return pix;
+}
+
+QImage qImageFromMimeSource(const QString &abs_name)
+{
+ const QMimeSource *m = Q3MimeSourceFactory::defaultFactory()->data(abs_name);
+ if (!m) {
+ qWarning("QImage::fromMimeSource: Cannot find image \"%s\" in the mime source factory", abs_name.latin1());
+ return QImage();
+ }
+ QImage img;
+ Q3ImageDrag::decode(m, img);
+ return img;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_MIMEFACTORY
diff --git a/src/qt3support/other/q3mimefactory.h b/src/qt3support/other/q3mimefactory.h
new file mode 100644
index 0000000..6d74534
--- /dev/null
+++ b/src/qt3support/other/q3mimefactory.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3MIMEFACTORY_H
+#define Q3MIMEFACTORY_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qmap.h>
+#include <QtGui/qpixmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_MIMEFACTORY
+
+class QStringList;
+class QMimeSource;
+class Q3MimeSourceFactoryData;
+
+class Q_COMPAT_EXPORT Q3MimeSourceFactory {
+public:
+ Q3MimeSourceFactory();
+ virtual ~Q3MimeSourceFactory();
+
+ static Q3MimeSourceFactory* defaultFactory();
+ static void setDefaultFactory(Q3MimeSourceFactory*);
+ static Q3MimeSourceFactory* takeDefaultFactory();
+ static void addFactory(Q3MimeSourceFactory *f);
+ static void removeFactory(Q3MimeSourceFactory *f);
+
+ virtual const QMimeSource* data(const QString& abs_name) const;
+ virtual QString makeAbsolute(const QString& abs_or_rel_name, const QString& context) const;
+ const QMimeSource* data(const QString& abs_or_rel_name, const QString& context) const;
+
+ virtual void setText(const QString& abs_name, const QString& text);
+ virtual void setImage(const QString& abs_name, const QImage& im);
+ virtual void setPixmap(const QString& abs_name, const QPixmap& pm);
+ virtual void setData(const QString& abs_name, QMimeSource* data);
+ virtual void setFilePath(const QStringList&);
+ inline void setFilePath(const QString &path) { setFilePath(QStringList(path)); }
+ virtual QStringList filePath() const;
+ void addFilePath(const QString&);
+ virtual void setExtensionType(const QString& ext, const char* mimetype);
+
+private:
+ QMimeSource *dataInternal(const QString& abs_name, const QMap<QString, QString> &extensions) const;
+ Q3MimeSourceFactoryData* d;
+};
+
+Q_COMPAT_EXPORT QPixmap qPixmapFromMimeSource(const QString &abs_name);
+
+Q_COMPAT_EXPORT QImage qImageFromMimeSource(const QString &abs_name);
+
+#endif // QT_NO_MIMEFACTORY
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3MIMEFACTORY_H
diff --git a/src/qt3support/other/q3polygonscanner.cpp b/src/qt3support/other/q3polygonscanner.cpp
new file mode 100644
index 0000000..36baf42
--- /dev/null
+++ b/src/qt3support/other/q3polygonscanner.cpp
@@ -0,0 +1,939 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3polygonscanner.h"
+#include "q3pointarray.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+// Based on Xserver code miFillGeneralPoly...
+/*
+ *
+ * Written by Brian Kelleher; Oct. 1985
+ *
+ * Routine to fill a polygon. Two fill rules are
+ * supported: frWINDING and frEVENODD.
+ *
+ * See fillpoly.h for a complete description of the algorithm.
+ */
+
+/*
+ * These are the data structures needed to scan
+ * convert regions. Two different scan conversion
+ * methods are available -- the even-odd method, and
+ * the winding number method.
+ * The even-odd rule states that a point is inside
+ * the polygon if a ray drawn from that point in any
+ * direction will pass through an odd number of
+ * path segments.
+ * By the winding number rule, a point is decided
+ * to be inside the polygon if a ray drawn from that
+ * point in any direction passes through a different
+ * number of clockwise and counterclockwise path
+ * segments.
+ *
+ * These data structures are adapted somewhat from
+ * the algorithm in (Foley/Van Dam) for scan converting
+ * polygons.
+ * The basic algorithm is to start at the top (smallest y)
+ * of the polygon, stepping down to the bottom of
+ * the polygon by incrementing the y coordinate. We
+ * keep a list of edges which the current scanline crosses,
+ * sorted by x. This list is called the Active Edge Table (AET)
+ * As we change the y-coordinate, we update each entry in
+ * in the active edge table to reflect the edges new xcoord.
+ * This list must be sorted at each scanline in case
+ * two edges intersect.
+ * We also keep a data structure known as the Edge Table (ET),
+ * which keeps track of all the edges which the current
+ * scanline has not yet reached. The ET is basically a
+ * list of ScanLineList structures containing a list of
+ * edges which are entered at a given scanline. There is one
+ * ScanLineList per scanline at which an edge is entered.
+ * When we enter a new edge, we move it from the ET to the AET.
+ *
+ * From the AET, we can implement the even-odd rule as in
+ * (Foley/Van Dam).
+ * The winding number rule is a little trickier. We also
+ * keep the EdgeTableEntries in the AET linked by the
+ * nextWETE (winding EdgeTableEntry) link. This allows
+ * the edges to be linked just as before for updating
+ * purposes, but only uses the edges linked by the nextWETE
+ * link as edges representing spans of the polygon to
+ * drawn (as with the even-odd rule).
+ */
+
+/* $XConsortium: miscanfill.h,v 1.5 94/04/17 20:27:50 dpw Exp $ */
+/*
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the X Consortium.
+
+*/
+
+
+/*
+ * scanfill.h
+ *
+ * Written by Brian Kelleher; Jan 1985
+ *
+ * This file contains a few macros to help track
+ * the edge of a filled object. The object is assumed
+ * to be filled in scanline order, and thus the
+ * algorithm used is an extension of Bresenham's line
+ * drawing algorithm which assumes that y is always the
+ * major axis.
+ * Since these pieces of code are the same for any filled shape,
+ * it is more convenient to gather the library in one
+ * place, but since these pieces of code are also in
+ * the inner loops of output primitives, procedure call
+ * overhead is out of the question.
+ * See the author for a derivation if needed.
+ */
+
+/*
+ * In scan converting polygons, we want to choose those pixels
+ * which are inside the polygon. Thus, we add .5 to the starting
+ * x coordinate for both left and right edges. Now we choose the
+ * first pixel which is inside the pgon for the left edge and the
+ * first pixel which is outside the pgon for the right edge.
+ * Draw the left pixel, but not the right.
+ *
+ * How to add .5 to the starting x coordinate:
+ * If the edge is moving to the right, then subtract dy from the
+ * error term from the general form of the algorithm.
+ * If the edge is moving to the left, then add dy to the error term.
+ *
+ * The reason for the difference between edges moving to the left
+ * and edges moving to the right is simple: If an edge is moving
+ * to the right, then we want the algorithm to flip immediately.
+ * If it is moving to the left, then we don't want it to flip until
+ * we traverse an entire pixel.
+ */
+#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
+ int dx; /* local storage */ \
+\
+ /* \
+ * if the edge is horizontal, then it is ignored \
+ * and assumed not to be processed. Otherwise, do this stuff. \
+ */ \
+ if ((dy) != 0) { \
+ xStart = (x1); \
+ dx = (x2) - xStart; \
+ if (dx < 0) { \
+ m = dx / (dy); \
+ m1 = m - 1; \
+ incr1 = -2 * dx + 2 * (dy) * m1; \
+ incr2 = -2 * dx + 2 * (dy) * m; \
+ d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
+ } else { \
+ m = dx / (dy); \
+ m1 = m + 1; \
+ incr1 = 2 * dx - 2 * (dy) * m1; \
+ incr2 = 2 * dx - 2 * (dy) * m; \
+ d = -2 * m * (dy) + 2 * dx; \
+ } \
+ } \
+}
+
+#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
+ if (m1 > 0) { \
+ if (d > 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } else {\
+ if (d >= 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } \
+}
+
+
+/*
+ * This structure contains all of the information needed
+ * to run the bresenham algorithm.
+ * The variables may be hardcoded into the declarations
+ * instead of using this structure to make use of
+ * register declarations.
+ */
+typedef struct {
+ int minor; /* minor axis */
+ int d; /* decision variable */
+ int m, m1; /* slope and slope+1 */
+ int incr1, incr2; /* error increments */
+} BRESINFO;
+
+
+#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
+ BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, \
+ bres.m, bres.m1, bres.incr1, bres.incr2)
+
+#define BRESINCRPGONSTRUCT(bres) \
+ BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2)
+
+
+typedef struct _EdgeTableEntry {
+ int ymax; /* ycoord at which we exit this edge. */
+ BRESINFO bres; /* Bresenham info to run the edge */
+ struct _EdgeTableEntry *next; /* next in the list */
+ struct _EdgeTableEntry *back; /* for insertion sort */
+ struct _EdgeTableEntry *nextWETE; /* for winding num rule */
+ int ClockWise; /* flag for winding number rule */
+} EdgeTableEntry;
+
+
+typedef struct _ScanLineList{
+ int scanline; /* the scanline represented */
+ EdgeTableEntry *edgelist; /* header node */
+ struct _ScanLineList *next; /* next in the list */
+} ScanLineList;
+
+
+typedef struct {
+ int ymax; /* ymax for the polygon */
+ int ymin; /* ymin for the polygon */
+ ScanLineList scanlines; /* header node */
+} EdgeTable;
+
+
+/*
+ * Here is a struct to help with storage allocation
+ * so we can allocate a big chunk at a time, and then take
+ * pieces from this heap when we need to.
+ */
+#define SLLSPERBLOCK 25
+
+typedef struct _ScanLineListBlock {
+ ScanLineList SLLs[SLLSPERBLOCK];
+ struct _ScanLineListBlock *next;
+} ScanLineListBlock;
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines() : Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ *
+ * a few macros for the inner loops of the fill code where
+ * performance considerations don't allow a procedure call.
+ *
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The winding number rule is in effect, so we must notify
+ * the caller when the edge has been removed so he
+ * can reorder the Winding Active Edge Table.
+ */
+#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ fixWAET = 1; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres); \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+
+
+/*
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The even-odd rule is in effect.
+ */
+#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres) \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+
+/***********************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#define MAXINT 0x7fffffff
+#define MININT -MAXINT
+
+/*
+ * fillUtils.c
+ *
+ * Written by Brian Kelleher; Oct. 1985
+ *
+ * This module contains all of the utility functions
+ * needed to scan convert a polygon.
+ *
+ */
+/*
+ * InsertEdgeInET
+ *
+ * Insert the given edge into the edge table.
+ * First we must find the correct bucket in the
+ * Edge table, then find the right slot in the
+ * bucket. Finally, we can insert it.
+ *
+ */
+static bool
+miInsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
+ int scanline, ScanLineListBlock **SLLBlock, int *iSLLBlock)
+{
+ register EdgeTableEntry *start, *prev;
+ register ScanLineList *pSLL, *pPrevSLL;
+ ScanLineListBlock *tmpSLLBlock;
+
+ /*
+ * find the right bucket to put the edge into
+ */
+ pPrevSLL = &ET->scanlines;
+ pSLL = pPrevSLL->next;
+ while (pSLL && (pSLL->scanline < scanline))
+ {
+ pPrevSLL = pSLL;
+ pSLL = pSLL->next;
+ }
+
+ /*
+ * reassign pSLL (pointer to ScanLineList) if necessary
+ */
+ if ((!pSLL) || (pSLL->scanline > scanline))
+ {
+ if (*iSLLBlock > SLLSPERBLOCK-1)
+ {
+ tmpSLLBlock =
+ (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock));
+ if (!tmpSLLBlock)
+ return false;
+ (*SLLBlock)->next = tmpSLLBlock;
+ tmpSLLBlock->next = 0;
+ *SLLBlock = tmpSLLBlock;
+ *iSLLBlock = 0;
+ }
+ pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
+
+ pSLL->next = pPrevSLL->next;
+ pSLL->edgelist = 0;
+ pPrevSLL->next = pSLL;
+ }
+ pSLL->scanline = scanline;
+
+ /*
+ * now insert the edge in the right bucket
+ */
+ prev = 0;
+ start = pSLL->edgelist;
+ while (start && (start->bres.minor < ETE->bres.minor))
+ {
+ prev = start;
+ start = start->next;
+ }
+ ETE->next = start;
+
+ if (prev)
+ prev->next = ETE;
+ else
+ pSLL->edgelist = ETE;
+ return true;
+}
+
+/*
+ * CreateEdgeTable
+ *
+ * This routine creates the edge table for
+ * scan converting polygons.
+ * The Edge Table (ET) looks like:
+ *
+ * EdgeTable
+ * --------
+ * | ymax | ScanLineLists
+ * |scanline|-->------------>-------------->...
+ * -------- |scanline| |scanline|
+ * |edgelist| |edgelist|
+ * --------- ---------
+ * | |
+ * | |
+ * V V
+ * list of ETEs list of ETEs
+ *
+ * where ETE is an EdgeTableEntry data structure,
+ * and there is one ScanLineList per scanline at
+ * which an edge is initially entered.
+ *
+ */
+
+typedef struct {
+#if defined(Q_OS_MAC)
+ int y, x;
+#else
+ int x, y;
+#endif
+
+} DDXPointRec, *DDXPointPtr;
+
+/*
+ * Clean up our act.
+ */
+static void
+miFreeStorage(ScanLineListBlock *pSLLBlock)
+{
+ register ScanLineListBlock *tmpSLLBlock;
+
+ while (pSLLBlock)
+ {
+ tmpSLLBlock = pSLLBlock->next;
+ free(pSLLBlock);
+ pSLLBlock = tmpSLLBlock;
+ }
+}
+
+static bool
+miCreateETandAET(int count, DDXPointPtr pts, EdgeTable *ET,
+ EdgeTableEntry *AET, EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
+{
+ register DDXPointPtr top, bottom;
+ register DDXPointPtr PrevPt, CurrPt;
+ int iSLLBlock = 0;
+
+ int dy;
+
+ if (count < 2) return true;
+
+ /*
+ * initialize the Active Edge Table
+ */
+ AET->next = 0;
+ AET->back = 0;
+ AET->nextWETE = 0;
+ AET->bres.minor = MININT;
+
+ /*
+ * initialize the Edge Table.
+ */
+ ET->scanlines.next = 0;
+ ET->ymax = MININT;
+ ET->ymin = MAXINT;
+ pSLLBlock->next = 0;
+
+ PrevPt = &pts[count-1];
+
+ /*
+ * for each vertex in the array of points.
+ * In this loop we are dealing with two vertices at
+ * a time -- these make up one edge of the polygon.
+ */
+ while (count--)
+ {
+ CurrPt = pts++;
+
+ /*
+ * find out which point is above and which is below.
+ */
+ if (PrevPt->y > CurrPt->y)
+ {
+ bottom = PrevPt, top = CurrPt;
+ pETEs->ClockWise = 0;
+ }
+ else
+ {
+ bottom = CurrPt, top = PrevPt;
+ pETEs->ClockWise = 1;
+ }
+
+ /*
+ * don't add horizontal edges to the Edge table.
+ */
+ if (bottom->y != top->y)
+ {
+ pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */
+
+ /*
+ * initialize integer edge algorithm
+ */
+ dy = bottom->y - top->y;
+ BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres)
+
+ if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock))
+ {
+ miFreeStorage(pSLLBlock->next);
+ return false;
+ }
+
+ ET->ymax = qMax(ET->ymax, PrevPt->y);
+ ET->ymin = qMin(ET->ymin, PrevPt->y);
+ pETEs++;
+ }
+
+ PrevPt = CurrPt;
+ }
+ return true;
+}
+
+/*
+ * loadAET
+ *
+ * This routine moves EdgeTableEntries from the
+ * EdgeTable into the Active Edge Table,
+ * leaving them sorted by smaller x coordinate.
+ *
+ */
+
+static void
+miloadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
+{
+ register EdgeTableEntry *pPrevAET;
+ register EdgeTableEntry *tmp;
+
+ pPrevAET = AET;
+ AET = AET->next;
+ while (ETEs)
+ {
+ while (AET && (AET->bres.minor < ETEs->bres.minor))
+ {
+ pPrevAET = AET;
+ AET = AET->next;
+ }
+ tmp = ETEs->next;
+ ETEs->next = AET;
+ if (AET)
+ AET->back = ETEs;
+ ETEs->back = pPrevAET;
+ pPrevAET->next = ETEs;
+ pPrevAET = ETEs;
+
+ ETEs = tmp;
+ }
+}
+
+/*
+ * computeWAET
+ *
+ * This routine links the AET by the
+ * nextWETE (winding EdgeTableEntry) link for
+ * use by the winding number rule. The final
+ * Active Edge Table (AET) might look something
+ * like:
+ *
+ * AET
+ * ---------- --------- ---------
+ * |ymax | |ymax | |ymax |
+ * | ... | |... | |... |
+ * |next |->|next |->|next |->...
+ * |nextWETE| |nextWETE| |nextWETE|
+ * --------- --------- ^--------
+ * | | |
+ * V-------------------> V---> ...
+ *
+ */
+static void
+micomputeWAET(EdgeTableEntry *AET)
+{
+ register EdgeTableEntry *pWETE;
+ register int inside = 1;
+ register int isInside = 0;
+
+ AET->nextWETE = 0;
+ pWETE = AET;
+ AET = AET->next;
+ while (AET)
+ {
+ if (AET->ClockWise)
+ isInside++;
+ else
+ isInside--;
+
+ if ((!inside && !isInside) ||
+ (inside && isInside))
+ {
+ pWETE->nextWETE = AET;
+ pWETE = AET;
+ inside = !inside;
+ }
+ AET = AET->next;
+ }
+ pWETE->nextWETE = 0;
+}
+
+/*
+ * InsertionSort
+ *
+ * Just a simple insertion sort using
+ * pointers and back pointers to sort the Active
+ * Edge Table.
+ *
+ */
+
+static int
+miInsertionSort(EdgeTableEntry *AET)
+{
+ register EdgeTableEntry *pETEchase;
+ register EdgeTableEntry *pETEinsert;
+ register EdgeTableEntry *pETEchaseBackTMP;
+ register int changed = 0;
+
+ AET = AET->next;
+ while (AET)
+ {
+ pETEinsert = AET;
+ pETEchase = AET;
+ while (pETEchase->back->bres.minor > AET->bres.minor)
+ pETEchase = pETEchase->back;
+
+ AET = AET->next;
+ if (pETEchase != pETEinsert)
+ {
+ pETEchaseBackTMP = pETEchase->back;
+ pETEinsert->back->next = AET;
+ if (AET)
+ AET->back = pETEinsert->back;
+ pETEinsert->next = pETEchase;
+ pETEchase->back->next = pETEinsert;
+ pETEchase->back = pETEinsert;
+ pETEinsert->back = pETEchaseBackTMP;
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/*!
+ \overload
+*/
+void Q3PolygonScanner::scan(const Q3PointArray& pa, bool winding, int index, int npoints)
+{
+ scan(pa, winding, index, npoints, true);
+}
+
+/*!
+ \overload
+
+ If \a stitchable is false, the right and bottom edges of the
+ polygon are included. This causes adjacent polygons to overlap.
+*/
+void Q3PolygonScanner::scan(const Q3PointArray& pa, bool winding, int index, int npoints, bool stitchable)
+{
+ scan(pa, winding, index, npoints,
+ stitchable ? Edge(Left+Top) : Edge(Left+Right+Top+Bottom));
+}
+
+/*!
+ Calls processSpans() for all scanlines of the polygon defined by
+ \a npoints starting at \a index in \a pa.
+
+ If \a winding is true, the Winding algorithm rather than the
+ Odd-Even rule is used.
+
+ The \a edges is any bitwise combination of:
+ \list
+ \i Q3PolygonScanner::Left
+ \i Q3PolygonScanner::Right
+ \i Q3PolygonScanner::Top
+ \i Q3PolygonScanner::Bottom
+ \endlist
+ \a edges determines which edges are included.
+
+ \warning The edges feature does not work properly.
+
+*/
+void Q3PolygonScanner::scan(const Q3PointArray& pa, bool winding, int index, int npoints, Edge edges)
+{
+
+
+ DDXPointPtr ptsIn = (DDXPointPtr)pa.data();
+ ptsIn += index;
+ register EdgeTableEntry *pAET; /* the Active Edge Table */
+ register int y; /* the current scanline */
+ register int nPts = 0; /* number of pts in buffer */
+ register EdgeTableEntry *pWETE; /* Winding Edge Table */
+ register ScanLineList *pSLL; /* Current ScanLineList */
+ register DDXPointPtr ptsOut; /* ptr to output buffers */
+ int *width;
+ DDXPointRec FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */
+ int FirstWidth[NUMPTSTOBUFFER];
+ EdgeTableEntry *pPrevAET; /* previous AET entry */
+ EdgeTable ET; /* Edge Table header node */
+ EdgeTableEntry AET; /* Active ET header node */
+ EdgeTableEntry *pETEs; /* Edge Table Entries buff */
+ ScanLineListBlock SLLBlock; /* header for ScanLineList */
+ int fixWAET = 0;
+ int edge_l = (edges & Left) ? 1 : 0;
+ int edge_r = (edges & Right) ? 1 : 0;
+ int edge_t = 1; //#### (edges & Top) ? 1 : 0;
+ int edge_b = (edges & Bottom) ? 1 : 0;
+
+ if (npoints == -1)
+ npoints = pa.size();
+
+ if (npoints < 3)
+ return;
+
+ if(!(pETEs = (EdgeTableEntry *)
+ malloc(sizeof(EdgeTableEntry) * npoints)))
+ return;
+ ptsOut = FirstPoint;
+ width = FirstWidth;
+ if (!miCreateETandAET(npoints, ptsIn, &ET, &AET, pETEs, &SLLBlock))
+ {
+ free(pETEs);
+ return;
+ }
+ pSLL = ET.scanlines.next;
+
+ if (!winding)
+ {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++)
+ {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline)
+ {
+ miloadAET(&AET, pSLL->edgelist);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+
+ /*
+ * for each active edge
+ */
+ while (pAET)
+ {
+ ptsOut->x = pAET->bres.minor + 1 - edge_l;
+ ptsOut++->y = y;
+ *width++ = pAET->next->bres.minor - pAET->bres.minor
+ - 1 + edge_l + edge_r;
+ nPts++;
+
+ /*
+ * send out the buffer when its full
+ */
+ if (nPts == NUMPTSTOBUFFER)
+ {
+ processSpans(nPts, (QPoint*)FirstPoint, FirstWidth);
+ ptsOut = FirstPoint;
+ width = FirstWidth;
+ nPts = 0;
+ }
+ EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+ EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+ }
+ miInsertionSort(&AET);
+ }
+ }
+ else /* default to WindingNumber */
+ {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++)
+ {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline)
+ {
+ miloadAET(&AET, pSLL->edgelist);
+ micomputeWAET(&AET);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+ pWETE = pAET;
+
+ /*
+ * for each active edge
+ */
+ while (pAET)
+ {
+ /*
+ * if the next edge in the active edge table is
+ * also the next edge in the winding active edge
+ * table.
+ */
+ if (pWETE == pAET)
+ {
+ ptsOut->x = pAET->bres.minor + 1 - edge_l;
+ ptsOut++->y = y;
+ *width++ = pAET->nextWETE->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r;
+ nPts++;
+
+ /*
+ * send out the buffer
+ */
+ if (nPts == NUMPTSTOBUFFER)
+ {
+ processSpans(nPts, (QPoint*)FirstPoint, FirstWidth);
+ ptsOut = FirstPoint;
+ width = FirstWidth;
+ nPts = 0;
+ }
+
+ pWETE = pWETE->nextWETE;
+ while (pWETE != pAET) {
+ EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+ }
+ pWETE = pWETE->nextWETE;
+ }
+ EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+ }
+
+ /*
+ * reevaluate the Winding active edge table if we
+ * just had to resort it or if we just exited an edge.
+ */
+ if (miInsertionSort(&AET) || fixWAET)
+ {
+ micomputeWAET(&AET);
+ fixWAET = 0;
+ }
+ }
+ }
+
+ /*
+ * Get any spans that we missed by buffering
+ */
+
+
+ processSpans(nPts, (QPoint*)FirstPoint, FirstWidth);
+ free(pETEs);
+ miFreeStorage(SLLBlock.next);
+}
+/***** END OF X11-based CODE *****/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/other/q3polygonscanner.h b/src/qt3support/other/q3polygonscanner.h
new file mode 100644
index 0000000..cf3d12f
--- /dev/null
+++ b/src/qt3support/other/q3polygonscanner.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3POLYGONSCANNER_H
+#define Q3POLYGONSCANNER_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3PointArray;
+class QPoint;
+
+class Q_COMPAT_EXPORT Q3PolygonScanner {
+public:
+ virtual ~Q3PolygonScanner() {}
+ void scan(const Q3PointArray& pa, bool winding, int index=0, int npoints=-1);
+ void scan(const Q3PointArray& pa, bool winding, int index, int npoints, bool stitchable);
+ enum Edge { Left=1, Right=2, Top=4, Bottom=8 };
+ void scan(const Q3PointArray& pa, bool winding, int index, int npoints, Edge edges);
+ virtual void processSpans(int n, QPoint* point, int* width)=0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3POLYGONSCANNER_H
diff --git a/src/qt3support/other/q3process.cpp b/src/qt3support/other/q3process.cpp
new file mode 100644
index 0000000..84e337f
--- /dev/null
+++ b/src/qt3support/other/q3process.cpp
@@ -0,0 +1,927 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3process.h"
+
+#ifndef QT_NO_PROCESS
+
+#include "qapplication.h"
+#include "private/q3membuf_p.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QT_Q3PROCESS_DEBUG
+
+
+/*!
+ \class Q3Process
+
+ \brief The Q3Process class is used to start external programs and
+ to communicate with them.
+
+ \compat
+
+ You can write to the started program's standard input, and can
+ read the program's standard output and standard error. You can
+ pass command line arguments to the program either in the
+ constructor or with setArguments() or addArgument(). The program's
+ working directory can be set with setWorkingDirectory(). If you
+ need to set up environment variables pass them to the start() or
+ launch() functions (see below). The processExited() signal is
+ emitted if the program exits. The program's exit status is
+ available from exitStatus(), although you could simply call
+ normalExit() to see if the program terminated normally.
+
+ There are two different ways to start a process. If you just want
+ to run a program, optionally passing data to its standard input at
+ the beginning, use one of the launch() functions. If you want full
+ control of the program's standard input (especially if you don't
+ know all the data you want to send to standard input at the
+ beginning), use the start() function.
+
+ If you use start() you can write to the program's standard input
+ using writeToStdin() and you can close the standard input with
+ closeStdin(). The wroteToStdin() signal is emitted if the data
+ sent to standard input has been written. You can read from the
+ program's standard output using readStdout() or readLineStdout().
+ These functions return an empty QByteArray if there is no data to
+ read. The readyReadStdout() signal is emitted when there is data
+ available to be read from standard output. Standard error has a
+ set of functions that correspond to the standard output functions,
+ i.e. readStderr(), readLineStderr() and readyReadStderr().
+
+ If you use one of the launch() functions the data you pass will be
+ sent to the program's standard input which will be closed once all
+ the data has been written. You should \e not use writeToStdin() or
+ closeStdin() if you use launch(). If you need to send data to the
+ program's standard input after it has started running use start()
+ instead of launch().
+
+ Both start() and launch() can accept a string list of strings each
+ of which has the format, key=value, where the keys are the names
+ of environment variables.
+
+ You can test to see if a program is running with isRunning(). The
+ program's process identifier is available from
+ processIdentifier(). If you want to terminate a running program
+ use tryTerminate(), but note that the program may ignore this. If
+ you \e really want to terminate the program, without it having any
+ chance to clean up, you can use kill().
+
+ Although you may need quotes for a file named on the command line
+ (e.g. if it contains spaces) you shouldn't use extra quotes for
+ arguments passed to addArgument() or setArguments().
+
+ The readyReadStdout() signal is emitted when there is new data on
+ standard output. This happens asynchronously: you don't know if
+ more data will arrive later.
+
+ In the above example you could connect the processExited() signal
+ to the slot UicManager::readFromStdout() instead. If you do so,
+ you will be certain that all the data is available when the slot
+ is called. On the other hand, you must wait until the process has
+ finished before doing any processing.
+
+ Note that if you are expecting a lot of output from the process,
+ you may hit platform-dependent limits to the pipe buffer size. The
+ solution is to make sure you connect to the output, e.g. the
+ readyReadStdout() and readyReadStderr() signals and read the data
+ as soon as it becomes available.
+
+ Please note that Q3Process does not emulate a shell. This means that
+ Q3Process does not do any expansion of arguments: a '*' is passed as a '*'
+ to the program and is \e not replaced by all the files, a '$HOME' is also
+ passed literally and is \e not replaced by the environment variable HOME
+ and the special characters for IO redirection ('>', '|', etc.) are also
+ passed literally and do \e not have the special meaning as they have in a
+ shell.
+
+ Also note that Q3Process does not emulate a terminal. This means that
+ certain programs which need direct terminal control, do not work as
+ expected with Q3Process. Such programs include console email programs (like
+ pine and mutt) but also programs which require the user to enter a password
+ (like su and ssh).
+
+ \section1 Notes for Windows users
+
+ Some Windows commands, for example, \c dir, are not provided by
+ separate applications, but by the command interpreter.
+ If you attempt to use Q3Process to execute these commands directly
+ it won't work. One possible solution is to execute the command
+ interpreter itself (\c cmd.exe on some Windows systems), and ask
+ the interpreter to execute the desired command.
+
+ Under Windows there are certain problems starting 16-bit applications
+ and capturing their output. Microsoft recommends using an intermediate
+ application to start 16-bit applications.
+
+ \sa Q3Socket
+*/
+
+/*!
+ \enum Q3Process::Communication
+
+ This enum type defines the communication channels connected to the
+ process.
+
+ \value Stdin Data can be written to the process's standard input.
+
+ \value Stdout Data can be read from the process's standard
+ output.
+
+ \value Stderr Data can be read from the process's standard error.
+
+ \value DupStderr Both the process's standard error output \e and
+ its standard output are written to its standard output. (Like
+ Unix's dup2().) This means that nothing is sent to the standard
+ error output. This is especially useful if your application
+ requires that the output on standard output and on standard error
+ must be read in the same order that they are produced. This is a
+ flag, so to activate it you must pass \c{Stdout|Stderr|DupStderr},
+ or \c{Stdin|Stdout|Stderr|DupStderr} if you want to provide input,
+ to the setCommunication() call.
+
+ \sa setCommunication() communication()
+*/
+
+/*!
+ Constructs a Q3Process object. The \a parent and \a name parameters
+ are passed to the QObject constructor.
+
+ \sa setArguments() addArgument() start()
+*/
+Q3Process::Q3Process( QObject *parent, const char *name )
+ : QObject( parent, name ), ioRedirection( false ), notifyOnExit( false ),
+ wroteToStdinConnected( false ),
+ readStdoutCalled( false ), readStderrCalled( false ),
+ comms( Stdin|Stdout|Stderr )
+{
+ init();
+}
+
+/*!
+ Constructs a Q3Process with \a arg0 as the command to be executed.
+ The \a parent and \a name parameters are passed to the QObject
+ constructor.
+
+ The process is not started. You must call start() or launch() to
+ start the process.
+
+ \sa setArguments() addArgument() start()
+*/
+Q3Process::Q3Process( const QString& arg0, QObject *parent, const char *name )
+ : QObject( parent, name ), ioRedirection( false ), notifyOnExit( false ),
+ wroteToStdinConnected( false ),
+ readStdoutCalled( false ), readStderrCalled( false ),
+ comms( Stdin|Stdout|Stderr )
+{
+ init();
+ addArgument( arg0 );
+}
+
+/*!
+ Constructs a Q3Process with \a args as the arguments of the
+ process. The first element in the list is the command to be
+ executed. The other elements in the list are the arguments to this
+ command. The \a parent and \a name parameters are passed to the
+ QObject constructor.
+
+ The process is not started. You must call start() or launch() to
+ start the process.
+
+ \sa setArguments() addArgument() start()
+*/
+Q3Process::Q3Process( const QStringList& args, QObject *parent, const char *name )
+ : QObject( parent, name ), ioRedirection( false ), notifyOnExit( false ),
+ wroteToStdinConnected( false ),
+ readStdoutCalled( false ), readStderrCalled( false ),
+ comms( Stdin|Stdout|Stderr )
+{
+ init();
+ setArguments( args );
+}
+
+/*!
+ \fn Q3Process::~Q3Process()
+
+ Destroys the instance.
+
+ If the process is running, it is <b>not</b> terminated! The
+ standard input, standard output and standard error of the process
+ are closed.
+
+ You can connect the destroyed() signal to the kill() slot, if you
+ want the process to be terminated automatically when the instance
+ is destroyed.
+
+ \sa tryTerminate() kill()
+*/
+
+/*!
+ Returns the list of arguments that are set for the process.
+ Arguments can be specified with the constructor or with the
+ functions setArguments() and addArgument().
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_other_q3process.cpp 0
+
+ \sa setArguments() addArgument()
+*/
+QStringList Q3Process::arguments() const
+{
+ return _arguments;
+}
+
+/*!
+ Clears the list of arguments that are set for the process.
+
+ \sa setArguments() addArgument()
+*/
+void Q3Process::clearArguments()
+{
+ _arguments.clear();
+}
+
+/*!
+ Sets \a args as the arguments for the process. The first element
+ in the list is the command to be executed. The other elements in
+ the list are the arguments to the command. Any previous arguments
+ are deleted.
+
+ Q3Process does not perform argument substitutions; for example, if you
+ specify "*" or "$DISPLAY", these values are passed to the process
+ literally. If you want to have the same behavior as the shell
+ provides, you must do the substitutions yourself; i.e. instead of
+ specifying a "*" you must specify the list of all the filenames in
+ the current directory, and instead of "$DISPLAY" you must specify
+ the value of the environment variable \c DISPLAY.
+
+ Note for Windows users. The standard Windows shells, e.g. \c
+ command.com and \c cmd.exe, do not perform file globbing, i.e.
+ they do not convert a "*" on the command line into a list of files
+ in the current directory. For this reason most Windows
+ applications implement their own file globbing, and as a result of
+ this, specifying an argument of "*" for a Windows application is
+ likely to result in the application performing a file glob and
+ ending up with a list of filenames.
+
+ \sa arguments() addArgument()
+*/
+void Q3Process::setArguments( const QStringList& args )
+{
+ _arguments = args;
+}
+
+/*!
+ Adds \a arg to the end of the list of arguments.
+
+ The first element in the list of arguments is the command to be
+ executed; the following elements are the command's arguments.
+
+ \sa arguments() setArguments()
+*/
+void Q3Process::addArgument( const QString& arg )
+{
+ _arguments.append( arg );
+}
+
+#ifndef QT_NO_DIR
+/*!
+ Returns the working directory that was set with
+ setWorkingDirectory(), or the current directory if none has been
+ explicitly set.
+
+ \sa setWorkingDirectory() QDir::current()
+*/
+QDir Q3Process::workingDirectory() const
+{
+ return workingDir;
+}
+
+/*!
+ Sets \a dir as the working directory for processes. This does not
+ affect running processes; only processes that are started
+ afterwards are affected.
+
+ Setting the working directory is especially useful for processes
+ that try to access files with relative paths.
+
+ \sa workingDirectory() start()
+*/
+void Q3Process::setWorkingDirectory( const QDir& dir )
+{
+ workingDir = dir;
+}
+#endif //QT_NO_DIR
+
+/*!
+ Returns the communication required with the process, i.e. some
+ combination of the \c Communication flags.
+
+ \sa setCommunication()
+*/
+int Q3Process::communication() const
+{
+ return comms;
+}
+
+/*!
+ Sets \a commFlags as the communication required with the process.
+
+ \a commFlags is a bitwise OR of the flags defined by the \c
+ Communication enum.
+
+ The default is \c{Stdin|Stdout|Stderr}.
+
+ \sa communication()
+*/
+void Q3Process::setCommunication( int commFlags )
+{
+ comms = commFlags;
+}
+
+/*!
+ Returns true if the process has exited normally; otherwise returns
+ false. This implies that this function returns false if the
+ process is still running.
+
+ \sa isRunning() exitStatus() processExited()
+*/
+bool Q3Process::normalExit() const
+{
+ // isRunning() has the side effect that it determines the exit status!
+ if ( isRunning() )
+ return false;
+ else
+ return exitNormal;
+}
+
+/*!
+ Returns the exit status of the process or 0 if the process is
+ still running. This function returns immediately and does not wait
+ until the process is finished.
+
+ If normalExit() is false (e.g. if the program was killed or
+ crashed), this function returns 0, so you should check the return
+ value of normalExit() before relying on this value.
+
+ \sa normalExit() processExited()
+*/
+int Q3Process::exitStatus() const
+{
+ // isRunning() has the side effect that it determines the exit status!
+ if ( isRunning() )
+ return 0;
+ else
+ return exitStat;
+}
+
+
+/*!
+ Reads the data that the process has written to standard output.
+ When new data is written to standard output, the class emits the
+ signal readyReadStdout().
+
+ If there is no data to read, this function returns a QByteArray of
+ size 0: it does not wait until there is something to read.
+
+ \sa readyReadStdout() readLineStdout() readStderr() writeToStdin()
+*/
+QByteArray Q3Process::readStdout()
+{
+ if ( readStdoutCalled ) {
+ return QByteArray();
+ }
+ readStdoutCalled = true;
+ Q3Membuf *buf = membufStdout();
+ readStdoutCalled = false;
+
+ return buf->readAll();
+}
+
+/*!
+ Reads the data that the process has written to standard error.
+ When new data is written to standard error, the class emits the
+ signal readyReadStderr().
+
+ If there is no data to read, this function returns a QByteArray of
+ size 0: it does not wait until there is something to read.
+
+ \sa readyReadStderr() readLineStderr() readStdout() writeToStdin()
+*/
+QByteArray Q3Process::readStderr()
+{
+ if ( readStderrCalled ) {
+ return QByteArray();
+ }
+ readStderrCalled = true;
+ Q3Membuf *buf = membufStderr();
+ readStderrCalled = false;
+
+ return buf->readAll();
+}
+
+/*!
+ Reads a line of text from standard output, excluding any trailing
+ newline or carriage return characters, and returns it. Returns
+ an empty string if canReadLineStdout() returns false.
+
+ By default, the text is interpreted to be in Latin-1 encoding. If you need
+ other codecs, you can set a different codec with
+ QTextCodec::setCodecForCStrings().
+
+ \sa canReadLineStdout() readyReadStdout() readStdout() readLineStderr()
+*/
+QString Q3Process::readLineStdout()
+{
+ QByteArray a( 256 );
+ Q3Membuf *buf = membufStdout();
+ if ( !buf->scanNewline( &a ) ) {
+ if ( !canReadLineStdout() )
+ return QString();
+
+ if ( !buf->scanNewline( &a ) )
+ return QLatin1String(buf->readAll());
+ }
+
+ uint size = a.size();
+ buf->consumeBytes( size, 0 );
+
+ // get rid of terminating \n or \r\n
+ if ( size>0 && a.at( size - 1 ) == '\n' ) {
+ if ( size>1 && a.at( size - 2 ) == '\r' )
+ a.chop(2);
+ else
+ a.chop(1);
+ }
+ return QString(QString::fromLatin1(a.constData()));
+}
+
+/*!
+ Reads a line of text from standard error, excluding any trailing
+ newline or carriage return characters and returns it. Returns
+ an empty string if canReadLineStderr() returns false.
+
+ By default, the text is interpreted to be in Latin-1 encoding. If you need
+ other codecs, you can set a different codec with
+ QTextCodec::setCodecForCStrings().
+
+ \sa canReadLineStderr() readyReadStderr() readStderr() readLineStdout()
+*/
+QString Q3Process::readLineStderr()
+{
+ QByteArray a( 256 );
+ Q3Membuf *buf = membufStderr();
+ if ( !buf->scanNewline( &a ) ) {
+ if ( !canReadLineStderr() )
+ return QString();
+
+ if ( !buf->scanNewline( &a ) )
+ return QString( QString::fromLatin1( buf->readAll().constData() ) );
+ }
+
+ uint size = a.size();
+ buf->consumeBytes( size, 0 );
+
+ // get rid of terminating \n or \r\n
+ if ( size>0 && a.at( size - 1 ) == '\n' ) {
+ if ( size>1 && a.at( size - 2 ) == '\r' )
+ a.chop(2);
+ else
+ a.chop(1);
+ }
+ return QString( QString::fromLatin1( a.constData() ) );
+}
+
+/*!
+ \fn bool Q3Process::start( QStringList *env )
+
+ Tries to run a process for the command and arguments that were
+ specified with setArguments(), addArgument() or that were
+ specified in the constructor. The command is searched for in the
+ path for executable programs; you can also use an absolute path in
+ the command itself.
+
+ If \a env is null, then the process is started with the same
+ environment as the starting process. If \a env is non-null, then
+ the values in the stringlist are interpreted as environment
+ setttings of the form \c {key=value} and the process is started in
+ these environment settings. For convenience, there is a small
+ exception to this rule: under Unix, if \a env does not contain any
+ settings for the environment variable \c LD_LIBRARY_PATH, then
+ this variable is inherited from the starting process; under
+ Windows the same applies for the environment variable \c PATH.
+
+ Returns true if the process could be started; otherwise returns
+ false.
+
+ You can write data to the process's standard input with
+ writeToStdin(). You can close standard input with closeStdin() and
+ you can terminate the process with tryTerminate(), or with kill().
+
+ You can call this function even if you've used this instance to
+ create a another process which is still running. In such cases,
+ Q3Process closes the old process's standard input and deletes
+ pending data, i.e., you lose all control over the old process, but
+ the old process is not terminated. This applies also if the
+ process could not be started. (On operating systems that have
+ zombie processes, Qt will also wait() on the old process.)
+
+ \sa launch() closeStdin()
+*/
+
+/*!
+ \fn void Q3Process::tryTerminate() const
+
+ Asks the process to terminate. Processes can ignore this if they
+ wish. If you want to be certain that the process really
+ terminates, you can use kill() instead.
+
+ The slot returns immediately: it does not wait until the process
+ has finished. When the process terminates, the processExited()
+ signal is emitted.
+
+ \sa kill() processExited()
+*/
+
+/*!
+ \fn void Q3Process::kill() const
+
+ Terminates the process. This is not a safe way to end a process
+ since the process will not be able to do any cleanup.
+ tryTerminate() is safer, but processes can ignore a
+ tryTerminate().
+
+ The nice way to end a process and to be sure that it is finished,
+ is to do something like this:
+ \snippet doc/src/snippets/code/src_qt3support_other_q3process_unix.cpp 0
+
+ This tries to terminate the process the nice way. If the process
+ is still running after 5 seconds, it terminates the process the
+ hard way. The timeout should be chosen depending on the time the
+ process needs to do all its cleanup: use a higher value if the
+ process is likely to do a lot of computation or I/O on cleanup.
+
+ The slot returns immediately: it does not wait until the process
+ has finished. When the process terminates, the processExited()
+ signal is emitted.
+
+ \sa tryTerminate() processExited()
+*/
+
+/*!
+ \fn bool Q3Process::isRunning() const
+
+ Returns true if the process is running; otherwise returns false.
+
+ \sa normalExit() exitStatus() processExited()
+*/
+
+/*!
+ \fn bool Q3Process::canReadLineStdout() const
+
+ Returns true if it's possible to read an entire line of text from
+ standard output at this time; otherwise returns false.
+
+ \sa readLineStdout() canReadLineStderr()
+*/
+
+/*!
+ \fn bool Q3Process::canReadLineStderr() const
+
+ Returns true if it's possible to read an entire line of text from
+ standard error at this time; otherwise returns false.
+
+ \sa readLineStderr() canReadLineStdout()
+*/
+
+/*!
+ \fn void Q3Process::writeToStdin( const QByteArray& buf )
+
+ Writes the data \a buf to the process's standard input. The
+ process may or may not read this data.
+
+ This function returns immediately; the Q3Process class might write
+ the data at a later point (you must enter the event loop for this
+ to occur). When all the data is written to the process, the signal
+ wroteToStdin() is emitted. This does not mean that the process
+ actually read the data, since this class only detects when it was
+ able to write the data to the operating system.
+
+ \sa wroteToStdin() closeStdin() readStdout() readStderr()
+*/
+
+/*!
+ \fn void Q3Process::closeStdin()
+
+ Closes the process's standard input.
+
+ This function also deletes any pending data that has not been
+ written to standard input.
+
+ \sa wroteToStdin()
+*/
+
+/*!
+ \fn Q3Process::PID Q3Process::processIdentifier()
+
+ Returns platform dependent information about the process. This can
+ be used together with platform specific system calls.
+
+ Under Unix the return value is the PID of the process, or -1 if no
+ process belongs to this object.
+
+ Under Windows it is a pointer to the \c PROCESS_INFORMATION
+ struct, or 0 if no process is belongs to this object.
+
+ Use of this function's return value is likely to be non-portable.
+*/
+
+/*!
+ \fn void Q3Process::launchFinished()
+
+ This signal is emitted when the process was started with launch().
+ If the start was successful, this signal is emitted after all the
+ data has been written to standard input. If the start failed, then
+ this signal is emitted immediately.
+
+ This signal is especially useful if you want to know when you can
+ safely delete the Q3Process object when you are not interested in
+ reading from standard output or standard error.
+
+ \sa launch() QObject::deleteLater()
+*/
+
+/*!
+ Runs the process and writes the data \a buf to the process's
+ standard input. If all the data is written to standard input,
+ standard input is closed. The command is searched for in the path
+ for executable programs; you can also use an absolute path in the
+ command itself.
+
+ If \a env is null, then the process is started with the same
+ environment as the starting process. If \a env is non-null, then
+ the values in the string list are interpreted as environment
+ setttings of the form \c {key=value} and the process is started
+ with these environment settings. For convenience, there is a small
+ exception to this rule under Unix: if \a env does not contain any
+ settings for the environment variable \c LD_LIBRARY_PATH, then
+ this variable is inherited from the starting process.
+
+ Returns true if the process could be started; otherwise returns
+ false.
+
+ Note that you should not use the slots writeToStdin() and
+ closeStdin() on processes started with launch(), since the result
+ is not well-defined. If you need these slots, use start() instead.
+
+ The process may or may not read the \a buf data sent to its
+ standard input.
+
+ You can call this function even when a process that was started
+ with this instance is still running. Be aware that if you do this
+ the standard input of the process that was launched first will be
+ closed, with any pending data being deleted, and the process will
+ be left to run out of your control. Similarly, if the process
+ could not be started the standard input will be closed and the
+ pending data deleted. (On operating systems that have zombie
+ processes, Qt will also wait() on the old process.)
+
+ The object emits the signal launchFinished() when this function
+ call is finished. If the start was successful, this signal is
+ emitted after all the data has been written to standard input. If
+ the start failed, then this signal is emitted immediately.
+
+ \sa start() launchFinished()
+*/
+bool Q3Process::launch( const QByteArray& buf, QStringList *env )
+{
+ if ( start( env ) ) {
+ if ( !buf.isEmpty() ) {
+ connect( this, SIGNAL(wroteToStdin()),
+ this, SLOT(closeStdinLaunch()) );
+ writeToStdin( buf );
+ } else {
+ closeStdin();
+ emit launchFinished();
+ }
+ return true;
+ } else {
+ emit launchFinished();
+ return false;
+ }
+}
+
+/*!
+ \overload
+
+ The data \a buf is written to standard input with writeToStdin()
+ using the QString::local8Bit() representation of the strings.
+*/
+bool Q3Process::launch( const QString& buf, QStringList *env )
+{
+ if ( start( env ) ) {
+ if ( !buf.isEmpty() ) {
+ connect( this, SIGNAL(wroteToStdin()),
+ this, SLOT(closeStdinLaunch()) );
+ writeToStdin( buf );
+ } else {
+ closeStdin();
+ emit launchFinished();
+ }
+ return true;
+ } else {
+ emit launchFinished();
+ return false;
+ }
+}
+
+/*
+ This private slot is used by the launch() functions to close standard input.
+*/
+void Q3Process::closeStdinLaunch()
+{
+ disconnect( this, SIGNAL(wroteToStdin()),
+ this, SLOT(closeStdinLaunch()) );
+ closeStdin();
+ emit launchFinished();
+}
+
+
+/*!
+ \fn void Q3Process::readyReadStdout()
+
+ This signal is emitted when the process has written data to
+ standard output. You can read the data with readStdout().
+
+ Note that this signal is only emitted when there is new data and
+ not when there is old, but unread data. In the slot connected to
+ this signal, you should always read everything that is available
+ at that moment to make sure that you don't lose any data.
+
+ \sa readStdout() readLineStdout() readyReadStderr()
+*/
+
+/*!
+ \fn void Q3Process::readyReadStderr()
+
+ This signal is emitted when the process has written data to
+ standard error. You can read the data with readStderr().
+
+ Note that this signal is only emitted when there is new data and
+ not when there is old, but unread data. In the slot connected to
+ this signal, you should always read everything that is available
+ at that moment to make sure that you don't lose any data.
+
+ \sa readStderr() readLineStderr() readyReadStdout()
+*/
+
+/*!
+ \fn void Q3Process::processExited()
+
+ This signal is emitted when the process has exited.
+
+ \sa isRunning() normalExit() exitStatus() start() launch()
+*/
+
+/*!
+ \fn void Q3Process::wroteToStdin()
+
+ This signal is emitted if the data sent to standard input (via
+ writeToStdin()) was actually written to the process. This does not
+ imply that the process really read the data, since this class only
+ detects when it was able to write the data to the operating
+ system. But it is now safe to close standard input without losing
+ pending data.
+
+ \sa writeToStdin() closeStdin()
+*/
+
+
+/*!
+ \overload
+
+ The string \a buf is handled as text using the
+ QString::local8Bit() representation.
+*/
+void Q3Process::writeToStdin( const QString& buf )
+{
+ QByteArray tmp = buf.local8Bit();
+ tmp.resize( buf.length() );
+ writeToStdin( tmp );
+}
+
+
+/*
+ * Under Windows the implementation is not so nice: it is not that easy to
+ * detect when one of the signals should be emitted; therefore there are some
+ * timers that query the information.
+ * To keep it a little efficient, use the timers only when they are needed.
+ * They are needed, if you are interested in the signals. So use
+ * connectNotify() and disconnectNotify() to keep track of your interest.
+ */
+/*! \reimp
+*/
+void Q3Process::connectNotify( const char * signal )
+{
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::connectNotify(): signal %s has been connected", signal );
+#endif
+ if ( !ioRedirection )
+ if ( qstrcmp( signal, SIGNAL(readyReadStdout()) )==0 ||
+ qstrcmp( signal, SIGNAL(readyReadStderr()) )==0
+ ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::connectNotify(): set ioRedirection to true" );
+#endif
+ setIoRedirection( true );
+ return;
+ }
+ if ( !notifyOnExit && qstrcmp( signal, SIGNAL(processExited()) )==0 ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::connectNotify(): set notifyOnExit to true" );
+#endif
+ setNotifyOnExit( true );
+ return;
+ }
+ if ( !wroteToStdinConnected && qstrcmp( signal, SIGNAL(wroteToStdin()) )==0 ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::connectNotify(): set wroteToStdinConnected to true" );
+#endif
+ setWroteStdinConnected( true );
+ return;
+ }
+}
+
+/*! \reimp
+*/
+void Q3Process::disconnectNotify( const char * )
+{
+ if ( ioRedirection &&
+ receivers( SIGNAL(readyReadStdout()) ) ==0 &&
+ receivers( SIGNAL(readyReadStderr()) ) ==0
+ ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::disconnectNotify(): set ioRedirection to false" );
+#endif
+ setIoRedirection( false );
+ }
+ if ( notifyOnExit && receivers( SIGNAL(processExited()) ) == 0 ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::disconnectNotify(): set notifyOnExit to false" );
+#endif
+ setNotifyOnExit( false );
+ }
+ if ( wroteToStdinConnected && receivers( SIGNAL(wroteToStdin()) ) == 0 ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::disconnectNotify(): set wroteToStdinConnected to false" );
+#endif
+ setWroteStdinConnected( false );
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROCESS
diff --git a/src/qt3support/other/q3process.h b/src/qt3support/other/q3process.h
new file mode 100644
index 0000000..8fac31a
--- /dev/null
+++ b/src/qt3support/other/q3process.h
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PROCESS_H
+#define Q3PROCESS_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdir.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_PROCESS
+
+class Q3ProcessPrivate;
+class Q3Membuf;
+
+class Q_COMPAT_EXPORT Q3Process : public QObject
+{
+ Q_OBJECT
+public:
+ Q3Process( QObject *parent=0, const char *name=0 );
+ Q3Process( const QString& arg0, QObject *parent=0, const char *name=0 );
+ Q3Process( const QStringList& args, QObject *parent=0, const char *name=0 );
+ ~Q3Process();
+
+ // set and get the arguments and working directory
+ QStringList arguments() const;
+ void clearArguments();
+ virtual void setArguments( const QStringList& args );
+ virtual void addArgument( const QString& arg );
+#ifndef QT_NO_DIR
+ QDir workingDirectory() const;
+ virtual void setWorkingDirectory( const QDir& dir );
+#endif
+
+ // set and get the comms wanted
+ enum Communication { Stdin=0x01, Stdout=0x02, Stderr=0x04, DupStderr=0x08 };
+ void setCommunication( int c );
+ int communication() const;
+
+ // start the execution
+ virtual bool start( QStringList *env=0 );
+ virtual bool launch( const QString& buf, QStringList *env=0 );
+ virtual bool launch( const QByteArray& buf, QStringList *env=0 );
+
+ // inquire the status
+ bool isRunning() const;
+ bool normalExit() const;
+ int exitStatus() const;
+
+ // reading
+ virtual QByteArray readStdout();
+ virtual QByteArray readStderr();
+ bool canReadLineStdout() const;
+ bool canReadLineStderr() const;
+ virtual QString readLineStdout();
+ virtual QString readLineStderr();
+
+ // get platform dependent process information
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ typedef void* PID;
+#else
+ typedef Q_LONG PID;
+#endif
+ PID processIdentifier();
+
+ void flushStdin();
+
+Q_SIGNALS:
+ void readyReadStdout();
+ void readyReadStderr();
+ void processExited();
+ void wroteToStdin();
+ void launchFinished();
+
+public Q_SLOTS:
+ // end the execution
+ void tryTerminate() const;
+ void kill() const;
+
+ // input
+ virtual void writeToStdin( const QByteArray& buf );
+ virtual void writeToStdin( const QString& buf );
+ virtual void closeStdin();
+
+protected: // ### or private?
+ void connectNotify( const char * signal );
+ void disconnectNotify( const char * signal );
+private:
+ void setIoRedirection( bool value );
+ void setNotifyOnExit( bool value );
+ void setWroteStdinConnected( bool value );
+
+ void init();
+ void reset();
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ uint readStddev( Qt::HANDLE dev, char *buf, uint bytes );
+#endif
+ Q3Membuf* membufStdout();
+ Q3Membuf* membufStderr();
+
+private Q_SLOTS:
+ void socketRead( int fd );
+ void socketWrite( int fd );
+ void timeout();
+ void closeStdinLaunch();
+
+private:
+ Q3ProcessPrivate *d;
+#ifndef QT_NO_DIR
+ QDir workingDir;
+#endif
+ QStringList _arguments;
+
+ int exitStat; // exit status
+ bool exitNormal; // normal exit?
+ bool ioRedirection; // automatically set be (dis)connectNotify
+ bool notifyOnExit; // automatically set be (dis)connectNotify
+ bool wroteToStdinConnected; // automatically set be (dis)connectNotify
+
+ bool readStdoutCalled;
+ bool readStderrCalled;
+ int comms;
+
+ friend class Q3ProcessPrivate;
+#if defined(Q_OS_UNIX)
+ friend class Q3ProcessManager;
+ friend class QProc;
+#endif
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ Q3Process( const Q3Process & );
+ Q3Process &operator=( const Q3Process & );
+#endif
+};
+
+#endif // QT_NO_PROCESS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PROCESS_H
diff --git a/src/qt3support/other/q3process_unix.cpp b/src/qt3support/other/q3process_unix.cpp
new file mode 100644
index 0000000..ac982d6
--- /dev/null
+++ b/src/qt3support/other/q3process_unix.cpp
@@ -0,0 +1,1283 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
+#if defined(connect)
+#undef connect
+#endif
+
+#include "q3process.h"
+
+#ifndef QT_NO_PROCESS
+
+#include "qapplication.h"
+#include "q3cstring.h"
+#include "q3ptrqueue.h"
+#include "q3ptrlist.h"
+#include "qsocketnotifier.h"
+#include "qtimer.h"
+#include "q3cleanuphandler.h"
+#include "qregexp.h"
+#include "private/q3membuf_p.h"
+#include "private/qobject_p.h"
+#include "private/qcore_unix_p.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef __MIPSEL__
+# ifndef SOCK_DGRAM
+# define SOCK_DGRAM 1
+# endif
+# ifndef SOCK_STREAM
+# define SOCK_STREAM 2
+# endif
+#endif
+
+//#define QT_Q3PROCESS_DEBUG
+
+
+#ifdef Q_C_CALLBACKS
+extern "C" {
+#endif // Q_C_CALLBACKS
+
+ static QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS);
+
+#ifdef Q_C_CALLBACKS
+}
+#endif // Q_C_CALLBACKS
+
+
+class QProc;
+class Q3ProcessManager;
+class Q3ProcessPrivate
+{
+public:
+ Q3ProcessPrivate();
+ ~Q3ProcessPrivate();
+
+ void closeOpenSocketsForChild();
+ void newProc( pid_t pid, Q3Process *process );
+
+ Q3Membuf bufStdout;
+ Q3Membuf bufStderr;
+
+ Q3PtrQueue<QByteArray> stdinBuf;
+
+ QSocketNotifier *notifierStdin;
+ QSocketNotifier *notifierStdout;
+ QSocketNotifier *notifierStderr;
+
+ ssize_t stdinBufRead;
+ QProc *proc;
+
+ bool exitValuesCalculated;
+ bool socketReadCalled;
+
+ static Q3ProcessManager *procManager;
+};
+
+
+/***********************************************************************
+ *
+ * QProc
+ *
+ **********************************************************************/
+/*
+ The class Q3Process does not necessarily map exactly to the running
+ child processes: if the process is finished, the Q3Process class may still be
+ there; furthermore a user can use Q3Process to start more than one process.
+
+ The helper-class QProc has the semantics that one instance of this class maps
+ directly to a running child process.
+*/
+class QProc
+{
+public:
+ QProc( pid_t p, Q3Process *proc=0 ) : pid(p), process(proc)
+ {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "QProc: Constructor for pid %d and Q3Process %p", pid, process );
+#endif
+ socketStdin = 0;
+ socketStdout = 0;
+ socketStderr = 0;
+ }
+ ~QProc()
+ {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "QProc: Destructor for pid %d and Q3Process %p", pid, process );
+#endif
+ if ( process ) {
+ if ( process->d->notifierStdin )
+ process->d->notifierStdin->setEnabled( false );
+ if ( process->d->notifierStdout )
+ process->d->notifierStdout->setEnabled( false );
+ if ( process->d->notifierStderr )
+ process->d->notifierStderr->setEnabled( false );
+ process->d->proc = 0;
+ }
+ if( socketStdin )
+ qt_safe_close( socketStdin );
+ if( socketStdout )
+ qt_safe_close( socketStdout );
+ if( socketStderr )
+ qt_safe_close( socketStderr );
+ }
+
+ pid_t pid;
+ int socketStdin;
+ int socketStdout;
+ int socketStderr;
+ Q3Process *process;
+};
+
+/***********************************************************************
+ *
+ * Q3ProcessManager
+ *
+ **********************************************************************/
+class Q3ProcessManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q3ProcessManager();
+ ~Q3ProcessManager();
+
+ void append( QProc *p );
+ void remove( QProc *p );
+
+ void cleanup();
+
+public slots:
+ void removeMe();
+ void sigchldHnd( int );
+
+public:
+ struct sigaction oldactChld;
+ struct sigaction oldactPipe;
+ Q3PtrList<QProc> *procList;
+ int sigchldFd[2];
+
+private:
+ QSocketNotifier *sn;
+};
+
+static void q3process_cleanup()
+{
+ delete Q3ProcessPrivate::procManager;
+ Q3ProcessPrivate::procManager = 0;
+}
+
+#ifdef Q_OS_QNX6
+#define BAILOUT qt_safe_close(tmpSocket);qt_safe_close(socketFD[1]);return -1;
+int qnx6SocketPairReplacement (int socketFD[2]) {
+ int tmpSocket;
+ tmpSocket = socket (AF_INET, SOCK_STREAM, 0);
+ if (tmpSocket == -1)
+ return -1;
+ socketFD[1] = socket(AF_INET, SOCK_STREAM, 0);
+ if (socketFD[1] == -1) { BAILOUT };
+
+ sockaddr_in ipAddr;
+ memset(&ipAddr, 0, sizeof(ipAddr));
+ ipAddr.sin_family = AF_INET;
+ ipAddr.sin_addr.s_addr = INADDR_ANY;
+
+ int socketOptions = 1;
+ setsockopt(tmpSocket, SOL_SOCKET, SO_REUSEADDR, &socketOptions, sizeof(int));
+
+ bool found = false;
+ for (int socketIP = 2000; (socketIP < 2500) && !(found); socketIP++) {
+ ipAddr.sin_port = htons(socketIP);
+ if (bind(tmpSocket, (struct sockaddr *)&ipAddr, sizeof(ipAddr)))
+ found = true;
+ }
+
+ if (listen(tmpSocket, 5)) { BAILOUT };
+
+ // Select non-blocking mode
+ int originalFlags = fcntl(socketFD[1], F_GETFL, 0);
+ fcntl(socketFD[1], F_SETFL, originalFlags | O_NONBLOCK);
+
+ // Request connection
+ if (connect(socketFD[1], (struct sockaddr*)&ipAddr, sizeof(ipAddr)))
+ if (errno != EINPROGRESS) { BAILOUT };
+
+ // Accept connection
+ socketFD[0] = accept(tmpSocket, (struct sockaddr *)NULL, (QT_SOCKLEN_T *)NULL);
+ if(socketFD[0] == -1) { BAILOUT };
+
+ // We're done
+ qt_safe_close(tmpSocket);
+
+ // Restore original flags , ie return to blocking
+ fcntl(socketFD[1], F_SETFL, originalFlags);
+ return 0;
+}
+#undef BAILOUT
+#endif
+
+Q3ProcessManager::Q3ProcessManager() : sn(0)
+{
+ procList = new Q3PtrList<QProc>;
+ procList->setAutoDelete( true );
+
+ // The SIGCHLD handler writes to a socket to tell the manager that
+ // something happened. This is done to get the processing in sync with the
+ // event reporting.
+#ifndef Q_OS_QNX6
+ if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sigchldFd ) ) {
+#else
+ if ( qnx6SocketPairReplacement (sigchldFd) ) {
+#endif
+ sigchldFd[0] = 0;
+ sigchldFd[1] = 0;
+ } else {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager: install socket notifier (%d)", sigchldFd[1] );
+#endif
+ sn = new QSocketNotifier( sigchldFd[1],
+ QSocketNotifier::Read, this );
+ connect( sn, SIGNAL(activated(int)),
+ this, SLOT(sigchldHnd(int)) );
+ sn->setEnabled( true );
+ }
+
+ // install a SIGCHLD handler and ignore SIGPIPE
+ struct sigaction act;
+
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager: install a SIGCHLD handler" );
+#endif
+ act.sa_handler = qt_C_sigchldHnd;
+ sigemptyset( &(act.sa_mask) );
+ sigaddset( &(act.sa_mask), SIGCHLD );
+ act.sa_flags = SA_NOCLDSTOP;
+#if defined(SA_RESTART)
+ act.sa_flags |= SA_RESTART;
+#endif
+ if ( sigaction( SIGCHLD, &act, &oldactChld ) != 0 )
+ qWarning( "Error installing SIGCHLD handler" );
+
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager: install a SIGPIPE handler (SIG_IGN)" );
+#endif
+ act.sa_handler = QT_SIGNAL_IGNORE;
+ sigemptyset( &(act.sa_mask) );
+ sigaddset( &(act.sa_mask), SIGPIPE );
+ act.sa_flags = 0;
+ if ( sigaction( SIGPIPE, &act, &oldactPipe ) != 0 )
+ qWarning( "Error installing SIGPIPE handler" );
+}
+
+Q3ProcessManager::~Q3ProcessManager()
+{
+ delete procList;
+
+ if ( sigchldFd[0] != 0 )
+ qt_safe_close( sigchldFd[0] );
+ if ( sigchldFd[1] != 0 )
+ qt_safe_close( sigchldFd[1] );
+
+ // restore SIGCHLD handler
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager: restore old sigchild handler" );
+#endif
+ if ( sigaction( SIGCHLD, &oldactChld, 0 ) != 0 )
+ qWarning( "Error restoring SIGCHLD handler" );
+
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager: restore old sigpipe handler" );
+#endif
+ if ( sigaction( SIGPIPE, &oldactPipe, 0 ) != 0 )
+ qWarning( "Error restoring SIGPIPE handler" );
+}
+
+void Q3ProcessManager::append( QProc *p )
+{
+ procList->append( p );
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager: append process (procList.count(): %d)", procList->count() );
+#endif
+}
+
+void Q3ProcessManager::remove( QProc *p )
+{
+ procList->remove( p );
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager: remove process (procList.count(): %d)", procList->count() );
+#endif
+ cleanup();
+}
+
+void Q3ProcessManager::cleanup()
+{
+ if ( procList->count() == 0 ) {
+ QTimer::singleShot( 0, this, SLOT(removeMe()) );
+ }
+}
+
+void Q3ProcessManager::removeMe()
+{
+ if ( procList->count() == 0 ) {
+ qRemovePostRoutine(q3process_cleanup);
+ Q3ProcessPrivate::procManager = 0;
+ delete this;
+ }
+}
+
+void Q3ProcessManager::sigchldHnd( int fd )
+{
+ // Disable the socket notifier to make sure that this function is not
+ // called recursively -- this can happen, if you enter the event loop in
+ // the slot connected to the processExited() signal (e.g. by showing a
+ // modal dialog) and there are more than one process which exited in the
+ // meantime.
+ if ( sn ) {
+ if ( !sn->isEnabled() )
+ return;
+ sn->setEnabled( false );
+ }
+
+ char tmp;
+ qt_safe_read( fd, &tmp, sizeof(tmp) );
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager::sigchldHnd()" );
+#endif
+ QProc *proc;
+ Q3Process *process;
+ bool removeProc;
+ proc = procList->first();
+ while ( proc != 0 ) {
+ removeProc = false;
+ process = proc->process;
+ if ( process != 0 ) {
+ if ( !process->isRunning() ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): process exited (Q3Process available)", proc->pid );
+#endif
+ /*
+ Apparently, there is not consistency among different
+ operating systems on how to use FIONREAD.
+
+ FreeBSD, Linux and Solaris all expect the 3rd
+ argument to ioctl() to be an int, which is normally
+ 32-bit even on 64-bit machines.
+
+ IRIX, on the other hand, expects a size_t, which is
+ 64-bit on 64-bit machines.
+
+ So, the solution is to use size_t initialized to
+ zero to make sure all bits are set to zero,
+ preventing underflow with the FreeBSD/Linux/Solaris
+ ioctls.
+ */
+ size_t nbytes = 0;
+ // read pending data
+ if ( proc->socketStdout && ::ioctl(proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stdout", proc->pid, nbytes );
+#endif
+ process->socketRead( proc->socketStdout );
+ }
+ nbytes = 0;
+ if ( proc->socketStderr && ::ioctl(proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stderr", proc->pid, nbytes );
+#endif
+ process->socketRead( proc->socketStderr );
+ }
+ // close filedescriptors if open, and disable the
+ // socket notifiers
+ if ( proc->socketStdout ) {
+ qt_safe_close( proc->socketStdout );
+ proc->socketStdout = 0;
+ if (process->d->notifierStdout)
+ process->d->notifierStdout->setEnabled(false);
+ }
+ if ( proc->socketStderr ) {
+ qt_safe_close( proc->socketStderr );
+ proc->socketStderr = 0;
+ if (process->d->notifierStderr)
+ process->d->notifierStderr->setEnabled(false);
+ }
+
+ if ( process->notifyOnExit )
+ emit process->processExited();
+
+ removeProc = true;
+ }
+ } else {
+ int status;
+ if ( ::waitpid( proc->pid, &status, WNOHANG ) == proc->pid ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): process exited (Q3Process not available)", proc->pid );
+#endif
+ removeProc = true;
+ }
+ }
+ if ( removeProc ) {
+ QProc *oldproc = proc;
+ proc = procList->next();
+ remove( oldproc );
+ } else {
+ proc = procList->next();
+ }
+ }
+ if ( sn )
+ sn->setEnabled( true );
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "q3process_unix.moc"
+QT_END_INCLUDE_NAMESPACE
+
+
+/***********************************************************************
+ *
+ * Q3ProcessPrivate
+ *
+ **********************************************************************/
+Q3ProcessManager *Q3ProcessPrivate::procManager = 0;
+
+Q3ProcessPrivate::Q3ProcessPrivate()
+{
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessPrivate: Constructor" );
+#endif
+ stdinBufRead = 0;
+
+ notifierStdin = 0;
+ notifierStdout = 0;
+ notifierStderr = 0;
+
+ exitValuesCalculated = false;
+ socketReadCalled = false;
+
+ proc = 0;
+}
+
+Q3ProcessPrivate::~Q3ProcessPrivate()
+{
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3ProcessPrivate: Destructor" );
+#endif
+
+ if ( proc != 0 ) {
+ if ( proc->socketStdin != 0 ) {
+ qt_safe_close( proc->socketStdin );
+ proc->socketStdin = 0;
+ }
+ proc->process = 0;
+ }
+
+ while ( !stdinBuf.isEmpty() ) {
+ delete stdinBuf.dequeue();
+ }
+ delete notifierStdin;
+ delete notifierStdout;
+ delete notifierStderr;
+}
+
+/*
+ Closes all open sockets in the child process that are not needed by the child
+ process. Otherwise one child may have an open socket on standard input, etc.
+ of another child.
+*/
+void Q3ProcessPrivate::closeOpenSocketsForChild()
+{
+ if ( procManager != 0 ) {
+ if ( procManager->sigchldFd[0] != 0 )
+ qt_safe_close( procManager->sigchldFd[0] );
+ if ( procManager->sigchldFd[1] != 0 )
+ qt_safe_close( procManager->sigchldFd[1] );
+
+ // close also the sockets from other Q3Process instances
+ for ( QProc *p=procManager->procList->first(); p!=0; p=procManager->procList->next() ) {
+ qt_safe_close( p->socketStdin );
+ qt_safe_close( p->socketStdout );
+ qt_safe_close( p->socketStderr );
+ }
+ }
+}
+
+void Q3ProcessPrivate::newProc( pid_t pid, Q3Process *process )
+{
+ proc = new QProc( pid, process );
+ if ( procManager == 0 ) {
+ procManager = new Q3ProcessManager;
+ qAddPostRoutine(q3process_cleanup);
+ }
+ // the Q3ProcessManager takes care of deleting the QProc instances
+ procManager->append( proc );
+}
+
+/***********************************************************************
+ *
+ * sigchld handler callback
+ *
+ **********************************************************************/
+static QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS)
+{
+ if ( Q3ProcessPrivate::procManager == 0 )
+ return;
+ if ( Q3ProcessPrivate::procManager->sigchldFd[0] == 0 )
+ return;
+
+ char a = 1;
+ qt_safe_write( Q3ProcessPrivate::procManager->sigchldFd[0], &a, sizeof(a) );
+}
+
+
+/***********************************************************************
+ *
+ * Q3Process
+ *
+ **********************************************************************/
+/*
+ This private class does basic initialization.
+*/
+void Q3Process::init()
+{
+ d = new Q3ProcessPrivate();
+ exitStat = 0;
+ exitNormal = false;
+}
+
+/*
+ This private class resets the process variables, etc. so that it can be used
+ for another process to start.
+*/
+void Q3Process::reset()
+{
+ delete d;
+ d = new Q3ProcessPrivate();
+ exitStat = 0;
+ exitNormal = false;
+ d->bufStdout.clear();
+ d->bufStderr.clear();
+}
+
+Q3Membuf* Q3Process::membufStdout()
+{
+ if ( d->proc && d->proc->socketStdout ) {
+ /*
+ Apparently, there is not consistency among different
+ operating systems on how to use FIONREAD.
+
+ FreeBSD, Linux and Solaris all expect the 3rd argument to
+ ioctl() to be an int, which is normally 32-bit even on
+ 64-bit machines.
+
+ IRIX, on the other hand, expects a size_t, which is 64-bit
+ on 64-bit machines.
+
+ So, the solution is to use size_t initialized to zero to
+ make sure all bits are set to zero, preventing underflow
+ with the FreeBSD/Linux/Solaris ioctls.
+ */
+ size_t nbytes = 0;
+ if ( ::ioctl(d->proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 )
+ socketRead( d->proc->socketStdout );
+ }
+ return &d->bufStdout;
+}
+
+Q3Membuf* Q3Process::membufStderr()
+{
+ if ( d->proc && d->proc->socketStderr ) {
+ /*
+ Apparently, there is not consistency among different
+ operating systems on how to use FIONREAD.
+
+ FreeBSD, Linux and Solaris all expect the 3rd argument to
+ ioctl() to be an int, which is normally 32-bit even on
+ 64-bit machines.
+
+ IRIX, on the other hand, expects a size_t, which is 64-bit
+ on 64-bit machines.
+
+ So, the solution is to use size_t initialized to zero to
+ make sure all bits are set to zero, preventing underflow
+ with the FreeBSD/Linux/Solaris ioctls.
+ */
+ size_t nbytes = 0;
+ if ( ::ioctl(d->proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 )
+ socketRead( d->proc->socketStderr );
+ }
+ return &d->bufStderr;
+}
+
+Q3Process::~Q3Process()
+{
+ delete d;
+}
+
+bool Q3Process::start( QStringList *env )
+{
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::start()" );
+#endif
+ reset();
+
+ int sStdin[2];
+ int sStdout[2];
+ int sStderr[2];
+
+ // open sockets for piping
+#ifndef Q_OS_QNX6
+ if ( (comms & Stdin) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) == -1 ) {
+#else
+ if ( (comms & Stdin) && qnx6SocketPairReplacement(sStdin) == -1 ) {
+#endif
+ return false;
+ }
+#ifndef Q_OS_QNX6
+ if ( (comms & Stderr) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) == -1 ) {
+#else
+ if ( (comms & Stderr) && qnx6SocketPairReplacement(sStderr) == -1 ) {
+#endif
+ if ( comms & Stdin ) {
+ qt_safe_close( sStdin[0] );
+ qt_safe_close( sStdin[1] );
+ }
+ return false;
+ }
+#ifndef Q_OS_QNX6
+ if ( (comms & Stdout) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) == -1 ) {
+#else
+ if ( (comms & Stdout) && qnx6SocketPairReplacement(sStdout) == -1 ) {
+#endif
+ if ( comms & Stdin ) {
+ qt_safe_close( sStdin[0] );
+ qt_safe_close( sStdin[1] );
+ }
+ if ( comms & Stderr ) {
+ qt_safe_close( sStderr[0] );
+ qt_safe_close( sStderr[1] );
+ }
+ return false;
+ }
+
+ // the following pipe is only used to determine if the process could be
+ // started
+ int fd[2];
+ if ( pipe( fd ) < 0 ) {
+ // non critical error, go on
+ fd[0] = 0;
+ fd[1] = 0;
+ }
+
+ // construct the arguments for exec
+ Q3CString *arglistQ = new Q3CString[ _arguments.count() + 1 ];
+ const char** arglist = new const char*[ _arguments.count() + 1 ];
+ int i = 0;
+ for ( QStringList::Iterator it = _arguments.begin(); it != _arguments.end(); ++it ) {
+ arglistQ[i] = (*it).local8Bit();
+ arglist[i] = arglistQ[i];
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::start(): arg %d = %s", i, arglist[i] );
+#endif
+ i++;
+ }
+#ifdef Q_OS_MACX
+ if(i) {
+ Q3CString arg_bundle = arglistQ[0];
+ QFileInfo fi(QString::fromUtf8(arg_bundle.constData()));
+ if(fi.exists() && fi.isDir() && arg_bundle.right(4) == ".app") {
+ Q3CString exe = arg_bundle;
+ int lslash = exe.findRev('/');
+ if(lslash != -1)
+ exe = exe.mid(lslash+1);
+ exe = Q3CString(arg_bundle + "/Contents/MacOS/" + exe);
+ exe = exe.left(exe.length() - 4); //chop off the .app
+ if(QFile::exists(QString::fromLatin1(exe.constData()))) {
+ arglistQ[0] = exe;
+ arglist[0] = arglistQ[0];
+ }
+ }
+ }
+#endif
+ arglist[i] = 0;
+
+ // Must make sure signal handlers are installed before exec'ing
+ // in case the process exits quickly.
+ if ( d->procManager == 0 ) {
+ d->procManager = new Q3ProcessManager;
+ qAddPostRoutine(q3process_cleanup);
+ }
+
+ // fork and exec
+ QApplication::flushX();
+ pid_t pid = fork();
+ if ( pid == 0 ) {
+ // child
+ d->closeOpenSocketsForChild();
+ if ( comms & Stdin ) {
+ qt_safe_close( sStdin[1] );
+ ::dup2( sStdin[0], STDIN_FILENO );
+ }
+ if ( comms & Stdout ) {
+ qt_safe_close( sStdout[0] );
+ ::dup2( sStdout[1], STDOUT_FILENO );
+ }
+ if ( comms & Stderr ) {
+ qt_safe_close( sStderr[0] );
+ ::dup2( sStderr[1], STDERR_FILENO );
+ }
+ if ( comms & DupStderr ) {
+ ::dup2( STDOUT_FILENO, STDERR_FILENO );
+ }
+#ifndef QT_NO_DIR
+ ::chdir( workingDir.absPath().latin1() );
+#endif
+ if ( fd[0] )
+ qt_safe_close( fd[0] );
+ if ( fd[1] )
+ ::fcntl( fd[1], F_SETFD, FD_CLOEXEC ); // close on exec shows success
+
+ if ( env == 0 ) { // inherit environment and start process
+#ifndef Q_OS_QNX4
+ ::execvp( arglist[0], (char*const*)arglist ); // ### cast not nice
+#else
+ ::execvp( arglist[0], (char const*const*)arglist ); // ### cast not nice
+#endif
+ } else { // start process with environment settins as specified in env
+ // construct the environment for exec
+ int numEntries = env->count();
+#if defined(Q_OS_MACX)
+ QString ld_library_path(QLatin1String("DYLD_LIBRARY_PATH"));
+#else
+ QString ld_library_path(QLatin1String("LD_LIBRARY_PATH"));
+#endif
+ bool setLibraryPath =
+ env->grep( QRegExp( QLatin1Char('^') + ld_library_path + QLatin1Char('=') ) ).empty() &&
+ getenv( ld_library_path.local8Bit() ) != 0;
+ if ( setLibraryPath )
+ numEntries++;
+ Q3CString *envlistQ = new Q3CString[ numEntries + 1 ];
+ const char** envlist = new const char*[ numEntries + 1 ];
+ int i = 0;
+ if ( setLibraryPath ) {
+ envlistQ[i] = QString( ld_library_path + QLatin1String("=%1") ).arg( QString::fromLocal8Bit(getenv( ld_library_path.local8Bit() )) ).local8Bit();
+ envlist[i] = envlistQ[i];
+ i++;
+ }
+ for ( QStringList::Iterator it = env->begin(); it != env->end(); ++it ) {
+ envlistQ[i] = (*it).local8Bit();
+ envlist[i] = envlistQ[i];
+ i++;
+ }
+ envlist[i] = 0;
+
+ // look for the executable in the search path
+ if ( _arguments.count()>0 && getenv("PATH")!=0 ) {
+ QString command = _arguments[0];
+ if ( !command.contains( QLatin1Char('/') ) ) {
+ QStringList pathList = QStringList::split( QLatin1Char(':'), QString::fromLocal8Bit(getenv( "PATH" )) );
+ for (QStringList::Iterator it = pathList.begin(); it != pathList.end(); ++it ) {
+ QString dir = *it;
+#if defined(Q_OS_MACX) //look in a bundle
+ if(!QFile::exists(dir + QLatin1Char('/') + command) && QFile::exists(dir + QLatin1Char('/') + command + QLatin1String(".app")))
+ dir += QLatin1Char('/') + command + QLatin1String(".app/Contents/MacOS");
+#endif
+#ifndef QT_NO_DIR
+ QFileInfo fileInfo( dir, command );
+#else
+ QFileInfo fileInfo( dir + QLatin1Char('/') + command );
+#endif
+ if ( fileInfo.isExecutable() ) {
+#if defined(Q_OS_MACX)
+ arglistQ[0] = fileInfo.absFilePath().local8Bit();
+#else
+ arglistQ[0] = fileInfo.filePath().local8Bit();
+#endif
+ arglist[0] = arglistQ[0];
+ break;
+ }
+ }
+ }
+ }
+#ifndef Q_OS_QNX4
+ ::execve( arglist[0], (char*const*)arglist, (char*const*)envlist ); // ### casts not nice
+#else
+ ::execve( arglist[0], (char const*const*)arglist,(char const*const*)envlist ); // ### casts not nice
+#endif
+ }
+ if ( fd[1] ) {
+ char buf = 0;
+ qt_safe_write( fd[1], &buf, 1 );
+ qt_safe_close( fd[1] );
+ }
+ ::_exit( -1 );
+ } else if ( pid == -1 ) {
+ // error forking
+ goto error;
+ }
+
+ // test if exec was successful
+ if ( fd[1] )
+ qt_safe_close( fd[1] );
+ if ( fd[0] ) {
+ char buf;
+ for ( ;; ) {
+ int n = ::read( fd[0], &buf, 1 );
+ if ( n==1 ) {
+ // socket was not closed => error
+ if ( ::waitpid( pid, 0, WNOHANG ) != pid ) {
+ // The wait did not succeed yet, so try again when we get
+ // the sigchild (to avoid zombies).
+ d->newProc( pid, 0 );
+ }
+ d->proc = 0;
+ goto error;
+ } else if ( n==-1 ) {
+ if ( errno==EAGAIN || errno==EINTR )
+ // try it again
+ continue;
+ }
+ break;
+ }
+ qt_safe_close( fd[0] );
+ }
+
+ d->newProc( pid, this );
+
+ if ( comms & Stdin ) {
+ qt_safe_close( sStdin[0] );
+ d->proc->socketStdin = sStdin[1];
+
+ // Select non-blocking mode
+ int originalFlags = fcntl(d->proc->socketStdin, F_GETFL, 0);
+ fcntl(d->proc->socketStdin, F_SETFL, originalFlags | O_NONBLOCK);
+
+ d->notifierStdin = new QSocketNotifier( sStdin[1], QSocketNotifier::Write );
+ connect( d->notifierStdin, SIGNAL(activated(int)),
+ this, SLOT(socketWrite(int)) );
+ // setup notifiers for the sockets
+ if ( !d->stdinBuf.isEmpty() ) {
+ d->notifierStdin->setEnabled( true );
+ }
+ }
+ if ( comms & Stdout ) {
+ qt_safe_close( sStdout[1] );
+ d->proc->socketStdout = sStdout[0];
+ d->notifierStdout = new QSocketNotifier( sStdout[0], QSocketNotifier::Read );
+ connect( d->notifierStdout, SIGNAL(activated(int)),
+ this, SLOT(socketRead(int)) );
+ if ( ioRedirection )
+ d->notifierStdout->setEnabled( true );
+ }
+ if ( comms & Stderr ) {
+ qt_safe_close( sStderr[1] );
+ d->proc->socketStderr = sStderr[0];
+ d->notifierStderr = new QSocketNotifier( sStderr[0], QSocketNotifier::Read );
+ connect( d->notifierStderr, SIGNAL(activated(int)),
+ this, SLOT(socketRead(int)) );
+ if ( ioRedirection )
+ d->notifierStderr->setEnabled( true );
+ }
+
+ // cleanup and return
+ delete[] arglistQ;
+ delete[] arglist;
+ return true;
+
+error:
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::start(): error starting process" );
+#endif
+ if ( d->procManager )
+ d->procManager->cleanup();
+ if ( comms & Stdin ) {
+ qt_safe_close( sStdin[1] );
+ qt_safe_close( sStdin[0] );
+ }
+ if ( comms & Stdout ) {
+ qt_safe_close( sStdout[0] );
+ qt_safe_close( sStdout[1] );
+ }
+ if ( comms & Stderr ) {
+ qt_safe_close( sStderr[0] );
+ qt_safe_close( sStderr[1] );
+ }
+ qt_safe_close( fd[0] );
+ qt_safe_close( fd[1] );
+ delete[] arglistQ;
+ delete[] arglist;
+ return false;
+}
+
+
+void Q3Process::tryTerminate() const
+{
+ if ( d->proc != 0 )
+ ::kill( d->proc->pid, SIGTERM );
+}
+
+void Q3Process::kill() const
+{
+ if ( d->proc != 0 )
+ ::kill( d->proc->pid, SIGKILL );
+}
+
+bool Q3Process::isRunning() const
+{
+ if ( d->exitValuesCalculated ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::isRunning(): false (already computed)" );
+#endif
+ return false;
+ }
+ if ( d->proc == 0 )
+ return false;
+ int status;
+ if ( ::waitpid( d->proc->pid, &status, WNOHANG ) == d->proc->pid ) {
+ // compute the exit values
+ Q3Process *that = (Q3Process*)this; // mutable
+ that->exitNormal = WIFEXITED( status ) != 0;
+ if ( exitNormal ) {
+ that->exitStat = (char)WEXITSTATUS( status );
+ }
+ d->exitValuesCalculated = true;
+
+ // On heavy processing, the socket notifier for the sigchild might not
+ // have found time to fire yet.
+ if ( d->procManager && d->procManager->sigchldFd[1] < FD_SETSIZE ) {
+ fd_set fds;
+ struct timeval tv;
+ FD_ZERO( &fds );
+ FD_SET( d->procManager->sigchldFd[1], &fds );
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ if ( ::select( d->procManager->sigchldFd[1]+1, &fds, 0, 0, &tv ) > 0 )
+ d->procManager->sigchldHnd( d->procManager->sigchldFd[1] );
+ }
+
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::isRunning() (PID: %d): false", d->proc->pid );
+#endif
+ return false;
+ }
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::isRunning() (PID: %d): true", d->proc->pid );
+#endif
+ return true;
+}
+
+bool Q3Process::canReadLineStdout() const
+{
+ if ( !d->proc || !d->proc->socketStdout )
+ return d->bufStdout.size() != 0;
+
+ Q3Process *that = (Q3Process*)this;
+ return that->membufStdout()->scanNewline( 0 );
+}
+
+bool Q3Process::canReadLineStderr() const
+{
+ if ( !d->proc || !d->proc->socketStderr )
+ return d->bufStderr.size() != 0;
+
+ Q3Process *that = (Q3Process*)this;
+ return that->membufStderr()->scanNewline( 0 );
+}
+
+void Q3Process::writeToStdin( const QByteArray& buf )
+{
+#if defined(QT_Q3PROCESS_DEBUG)
+// qDebug( "Q3Process::writeToStdin(): write to stdin (%d)", d->socketStdin );
+#endif
+ d->stdinBuf.enqueue( new QByteArray(buf) );
+ if ( d->notifierStdin != 0 )
+ d->notifierStdin->setEnabled( true );
+}
+
+
+void Q3Process::closeStdin()
+{
+ if ( d->proc == 0 )
+ return;
+ if ( d->proc->socketStdin !=0 ) {
+ while ( !d->stdinBuf.isEmpty() ) {
+ delete d->stdinBuf.dequeue();
+ }
+ d->notifierStdin->setEnabled(false);
+ qDeleteInEventHandler(d->notifierStdin);
+ d->notifierStdin = 0;
+ if ( qt_safe_close( d->proc->socketStdin ) != 0 ) {
+ qWarning( "Could not close stdin of child process" );
+ }
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::closeStdin(): stdin (%d) closed", d->proc->socketStdin );
+#endif
+ d->proc->socketStdin = 0;
+ }
+}
+
+
+/*
+ This private slot is called when the process has outputted data to either
+ standard output or standard error.
+*/
+void Q3Process::socketRead( int fd )
+{
+ if ( d->socketReadCalled ) {
+ // the slots that are connected to the readyRead...() signals might
+ // trigger a recursive call of socketRead(). Avoid this since you get a
+ // blocking read otherwise.
+ return;
+ }
+
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::socketRead(): %d", fd );
+#endif
+ if ( fd == 0 )
+ return;
+ if ( !d->proc )
+ return;
+ Q3Membuf *buffer = 0;
+ int n;
+ if ( fd == d->proc->socketStdout ) {
+ buffer = &d->bufStdout;
+ } else if ( fd == d->proc->socketStderr ) {
+ buffer = &d->bufStderr;
+ } else {
+ // this case should never happen, but just to be safe
+ return;
+ }
+#if defined(QT_Q3PROCESS_DEBUG)
+ uint oldSize = buffer->size();
+#endif
+
+ // try to read data first (if it fails, the filedescriptor was closed)
+ const int basize = 4096;
+ QByteArray *ba = new QByteArray( basize );
+ n = ::read( fd, ba->data(), basize );
+ if ( n > 0 ) {
+ ba->resize( n );
+ buffer->append( ba );
+ ba = 0;
+ } else {
+ delete ba;
+ ba = 0;
+ }
+ // eof or error?
+ if ( n == 0 || n == -1 ) {
+ if ( fd == d->proc->socketStdout ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::socketRead(): stdout (%d) closed", fd );
+#endif
+ d->notifierStdout->setEnabled( false );
+ qDeleteInEventHandler(d->notifierStdout);
+ d->notifierStdout = 0;
+ qt_safe_close( d->proc->socketStdout );
+ d->proc->socketStdout = 0;
+ return;
+ } else if ( fd == d->proc->socketStderr ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::socketRead(): stderr (%d) closed", fd );
+#endif
+ d->notifierStderr->setEnabled( false );
+ qDeleteInEventHandler(d->notifierStderr);
+ d->notifierStderr = 0;
+ qt_safe_close( d->proc->socketStderr );
+ d->proc->socketStderr = 0;
+ return;
+ }
+ }
+
+ if ( fd < FD_SETSIZE ) {
+ fd_set fds;
+ struct timeval tv;
+ FD_ZERO( &fds );
+ FD_SET( fd, &fds );
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ while ( ::select( fd+1, &fds, 0, 0, &tv ) > 0 ) {
+ // prepare for the next round
+ FD_ZERO( &fds );
+ FD_SET( fd, &fds );
+ // read data
+ ba = new QByteArray( basize );
+ n = ::read( fd, ba->data(), basize );
+ if ( n > 0 ) {
+ ba->resize( n );
+ buffer->append( ba );
+ ba = 0;
+ } else {
+ delete ba;
+ ba = 0;
+ break;
+ }
+ }
+ }
+
+ d->socketReadCalled = true;
+ if ( fd == d->proc->socketStdout ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::socketRead(): %d bytes read from stdout (%d)",
+ buffer->size()-oldSize, fd );
+#endif
+ emit readyReadStdout();
+ } else if ( fd == d->proc->socketStderr ) {
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::socketRead(): %d bytes read from stderr (%d)",
+ buffer->size()-oldSize, fd );
+#endif
+ emit readyReadStderr();
+ }
+ d->socketReadCalled = false;
+}
+
+
+/*
+ This private slot is called when the process tries to read data from standard
+ input.
+*/
+void Q3Process::socketWrite( int fd )
+{
+ while ( fd == d->proc->socketStdin && d->proc->socketStdin != 0 ) {
+ if ( d->stdinBuf.isEmpty() ) {
+ d->notifierStdin->setEnabled( false );
+ return;
+ }
+ ssize_t ret = ::write( fd,
+ d->stdinBuf.head()->data() + d->stdinBufRead,
+ d->stdinBuf.head()->size() - d->stdinBufRead );
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::socketWrite(): wrote %d bytes to stdin (%d)", ret, fd );
+#endif
+ if ( ret == -1 )
+ return;
+ d->stdinBufRead += ret;
+ if ( d->stdinBufRead == (ssize_t)d->stdinBuf.head()->size() ) {
+ d->stdinBufRead = 0;
+ delete d->stdinBuf.dequeue();
+ if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
+ emit wroteToStdin();
+ }
+ }
+}
+
+/*!
+ \internal
+ Flushes standard input. This is useful if you want to use Q3Process in a
+ synchronous manner.
+
+ This function should probably go into the public API.
+*/
+void Q3Process::flushStdin()
+{
+ if (d->proc)
+ socketWrite(d->proc->socketStdin);
+}
+
+/*
+ This private slot is only used under Windows (but moc does not know about #if
+ defined()).
+*/
+void Q3Process::timeout()
+{
+}
+
+
+/*
+ This private function is used by connectNotify() and disconnectNotify() to
+ change the value of ioRedirection (and related behaviour)
+*/
+void Q3Process::setIoRedirection( bool value )
+{
+ ioRedirection = value;
+ if ( ioRedirection ) {
+ if ( d->notifierStdout )
+ d->notifierStdout->setEnabled( true );
+ if ( d->notifierStderr )
+ d->notifierStderr->setEnabled( true );
+ } else {
+ if ( d->notifierStdout )
+ d->notifierStdout->setEnabled( false );
+ if ( d->notifierStderr )
+ d->notifierStderr->setEnabled( false );
+ }
+}
+
+/*
+ This private function is used by connectNotify() and
+ disconnectNotify() to change the value of notifyOnExit (and related
+ behaviour)
+*/
+void Q3Process::setNotifyOnExit( bool value )
+{
+ notifyOnExit = value;
+}
+
+/*
+ This private function is used by connectNotify() and disconnectNotify() to
+ change the value of wroteToStdinConnected (and related behaviour)
+*/
+void Q3Process::setWroteStdinConnected( bool value )
+{
+ wroteToStdinConnected = value;
+}
+
+/*!
+ \typedef Q3Process::PID
+ \internal
+*/
+
+Q3Process::PID Q3Process::processIdentifier()
+{
+ if ( d->proc == 0 )
+ return -1;
+ return d->proc->pid;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROCESS
diff --git a/src/qt3support/other/q3process_win.cpp b/src/qt3support/other/q3process_win.cpp
new file mode 100644
index 0000000..05132e9
--- /dev/null
+++ b/src/qt3support/other/q3process_win.cpp
@@ -0,0 +1,628 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "q3process.h"
+
+#ifndef QT_NO_PROCESS
+
+#include "qapplication.h"
+#include "q3cstring.h"
+#include "q3ptrqueue.h"
+#include "qtimer.h"
+#include "qregexp.h"
+#include "private/q3membuf_p.h"
+#include "qt_windows.h"
+
+#ifdef Q_OS_WINCE
+#define STARTF_USESTDHANDLES 1
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define QT_Q3PROCESS_DEBUG
+
+/***********************************************************************
+ *
+ * Q3ProcessPrivate
+ *
+ **********************************************************************/
+class Q3ProcessPrivate
+{
+public:
+ Q3ProcessPrivate( Q3Process *proc )
+ {
+ stdinBufRead = 0;
+ pipeStdin[0] = 0;
+ pipeStdin[1] = 0;
+ pipeStdout[0] = 0;
+ pipeStdout[1] = 0;
+ pipeStderr[0] = 0;
+ pipeStderr[1] = 0;
+ exitValuesCalculated = false;
+
+ lookup = new QTimer( proc );
+ qApp->connect( lookup, SIGNAL(timeout()),
+ proc, SLOT(timeout()) );
+
+ pid = 0;
+ }
+
+ ~Q3ProcessPrivate()
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ while ( !stdinBuf.isEmpty() ) {
+ delete stdinBuf.dequeue();
+ }
+ closeHandles();
+ stdinBufRead = 0;
+ pipeStdin[0] = 0;
+ pipeStdin[1] = 0;
+ pipeStdout[0] = 0;
+ pipeStdout[1] = 0;
+ pipeStderr[0] = 0;
+ pipeStderr[1] = 0;
+ exitValuesCalculated = false;
+
+ deletePid();
+ }
+
+ void closeHandles()
+ {
+ if( pipeStdin[1] != 0 ) {
+ CloseHandle( pipeStdin[1] );
+ pipeStdin[1] = 0;
+ }
+ if( pipeStdout[0] != 0 ) {
+ CloseHandle( pipeStdout[0] );
+ pipeStdout[0] = 0;
+ }
+ if( pipeStderr[0] != 0 ) {
+ CloseHandle( pipeStderr[0] );
+ pipeStderr[0] = 0;
+ }
+ }
+
+ void deletePid()
+ {
+ if ( pid ) {
+ CloseHandle( pid->hProcess );
+ CloseHandle( pid->hThread );
+ delete pid;
+ pid = 0;
+ }
+ }
+
+ void newPid()
+ {
+ deletePid();
+ pid = new PROCESS_INFORMATION;
+ memset( pid, 0, sizeof(PROCESS_INFORMATION) );
+ }
+
+ Q3Membuf bufStdout;
+ Q3Membuf bufStderr;
+
+ Q3PtrQueue<QByteArray> stdinBuf;
+
+ HANDLE pipeStdin[2];
+ HANDLE pipeStdout[2];
+ HANDLE pipeStderr[2];
+ QTimer *lookup;
+
+ PROCESS_INFORMATION *pid;
+ uint stdinBufRead;
+
+ bool exitValuesCalculated;
+};
+
+
+/***********************************************************************
+ *
+ * Q3Process
+ *
+ **********************************************************************/
+void Q3Process::init()
+{
+ d = new Q3ProcessPrivate( this );
+ exitStat = 0;
+ exitNormal = false;
+}
+
+void Q3Process::reset()
+{
+ d->reset();
+ exitStat = 0;
+ exitNormal = false;
+ d->bufStdout.clear();
+ d->bufStderr.clear();
+}
+
+Q3Membuf* Q3Process::membufStdout()
+{
+ if( d->pipeStdout[0] != 0 )
+ socketRead( 1 );
+ return &d->bufStdout;
+}
+
+Q3Membuf* Q3Process::membufStderr()
+{
+ if( d->pipeStderr[0] != 0 )
+ socketRead( 2 );
+ return &d->bufStderr;
+}
+
+Q3Process::~Q3Process()
+{
+ delete d;
+}
+
+bool Q3Process::start( QStringList *env )
+{
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::start()" );
+#endif
+ reset();
+
+ if ( _arguments.isEmpty() )
+ return false;
+
+ // Open the pipes. Make non-inheritable copies of input write and output
+ // read handles to avoid non-closable handles (this is done by the
+ // DuplicateHandle() call).
+ SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
+#ifndef Q_OS_WINCE
+ // I guess there is no stdin stdout and stderr on Q_OS_WINCE to dup
+ // CreatePipe and DupilcateHandle aren't available for Q_OS_WINCE
+ HANDLE tmpStdin, tmpStdout, tmpStderr;
+ if ( comms & Stdin ) {
+ if ( !CreatePipe( &d->pipeStdin[0], &tmpStdin, &secAtt, 0 ) ) {
+ d->closeHandles();
+ return false;
+ }
+ if ( !DuplicateHandle( GetCurrentProcess(), tmpStdin, GetCurrentProcess(), &d->pipeStdin[1], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) {
+ d->closeHandles();
+ return false;
+ }
+ if ( !CloseHandle( tmpStdin ) ) {
+ d->closeHandles();
+ return false;
+ }
+ }
+ if ( comms & Stdout ) {
+ if ( !CreatePipe( &tmpStdout, &d->pipeStdout[1], &secAtt, 0 ) ) {
+ d->closeHandles();
+ return false;
+ }
+ if ( !DuplicateHandle( GetCurrentProcess(), tmpStdout, GetCurrentProcess(), &d->pipeStdout[0], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) {
+ d->closeHandles();
+ return false;
+ }
+ if ( !CloseHandle( tmpStdout ) ) {
+ d->closeHandles();
+ return false;
+ }
+ }
+ if ( comms & Stderr ) {
+ if ( !CreatePipe( &tmpStderr, &d->pipeStderr[1], &secAtt, 0 ) ) {
+ d->closeHandles();
+ return false;
+ }
+ if ( !DuplicateHandle( GetCurrentProcess(), tmpStderr, GetCurrentProcess(), &d->pipeStderr[0], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) {
+ d->closeHandles();
+ return false;
+ }
+ if ( !CloseHandle( tmpStderr ) ) {
+ d->closeHandles();
+ return false;
+ }
+ }
+ if ( comms & DupStderr ) {
+ CloseHandle( d->pipeStderr[1] );
+ d->pipeStderr[1] = d->pipeStdout[1];
+ }
+#endif
+
+ // construct the arguments for CreateProcess()
+ QString args;
+ QString appName;
+ QStringList::Iterator it = _arguments.begin();
+ args = *it;
+ ++it;
+ if ( args.endsWith( QLatin1String(".bat") ) && args.contains( QLatin1Char(' ') ) ) {
+ // CreateProcess() seems to have a strange semantics (see also
+ // http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_11138647.html):
+ // If you start a batch file with spaces in the filename, the first
+ // argument to CreateProcess() must be the name of the batchfile
+ // without quotes, but the second argument must start with the same
+ // argument with quotes included. But if the same approach is used for
+ // .exe files, it doesn't work.
+ appName = args;
+ args = QLatin1Char('"') + args + QLatin1Char('"');
+ }
+ for ( ; it != _arguments.end(); ++it ) {
+ QString tmp = *it;
+ // escape a single " because the arguments will be parsed
+ tmp.replace( QLatin1Char('\"'), QLatin1String("\\\"") );
+ if ( tmp.isEmpty() || tmp.contains( QLatin1Char(' ') ) || tmp.contains( QLatin1Char('\t') ) ) {
+ // The argument must not end with a \ since this would be interpreted
+ // as escaping the quote -- rather put the \ behind the quote: e.g.
+ // rather use "foo"\ than "foo\"
+ QString endQuote( QLatin1String("\"") );
+ int i = tmp.length();
+ while ( i>0 && tmp.at( i-1 ) == QLatin1Char('\\') ) {
+ --i;
+ endQuote += QLatin1Char('\\');
+ }
+ args += QLatin1String(" \"") + tmp.left( i ) + endQuote;
+ } else {
+ args += QLatin1Char(' ') + tmp;
+ }
+ }
+#if defined(QT_Q3PROCESS_DEBUG)
+ qDebug( "Q3Process::start(): args [%s]", args.latin1() );
+#endif
+
+ // CreateProcess()
+ bool success;
+ d->newPid();
+
+ STARTUPINFOW startupInfo = {
+ sizeof( STARTUPINFO ), 0, 0, 0,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ 0, 0, 0,
+ STARTF_USESTDHANDLES,
+ 0, 0, 0,
+ d->pipeStdin[0], d->pipeStdout[1], d->pipeStderr[1]
+ };
+ wchar_t *applicationName;
+ if ( appName.isNull() )
+ applicationName = 0;
+ else
+ applicationName = _wcsdup( (wchar_t*)appName.utf16() );
+ wchar_t *commandLine = _wcsdup( (wchar_t*)args.utf16() );
+ QByteArray envlist;
+ if ( env != 0 ) {
+ int pos = 0;
+ // add PATH if necessary (for DLL loading)
+ QByteArray path = qgetenv( "PATH" );
+ if ( env->grep( QRegExp(QLatin1String("^PATH="),FALSE) ).empty() && !path.isNull() ) {
+ QString tmp = QString::fromLatin1("PATH=%1").arg(QLatin1String(path.constData()));
+ uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
+ envlist.resize( envlist.size() + tmpSize );
+ memcpy( envlist.data() + pos, tmp.utf16(), tmpSize );
+ pos += tmpSize;
+ }
+ // add the user environment
+ for ( QStringList::Iterator it = env->begin(); it != env->end(); it++ ) {
+ QString tmp = *it;
+ uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
+ envlist.resize( envlist.size() + tmpSize );
+ memcpy( envlist.data() + pos, tmp.utf16(), tmpSize );
+ pos += tmpSize;
+ }
+ // add the 2 terminating 0 (actually 4, just to be on the safe side)
+ envlist.resize( envlist.size()+4 );
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ }
+ success = CreateProcess( applicationName, commandLine,
+ 0, 0, TRUE, ( comms == 0 ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW )
+#ifndef Q_OS_WINCE
+ | CREATE_UNICODE_ENVIRONMENT
+#endif
+ , env == 0 ? 0 : envlist.data(),
+ (wchar_t*)QDir::toNativeSeparators(workingDir.absPath()).utf16(),
+ &startupInfo, d->pid );
+
+ free( applicationName );
+ free( commandLine );
+
+ if ( !success ) {
+ d->deletePid();
+ return false;
+ }
+
+#ifndef Q_OS_WINCE
+ if ( comms & Stdin )
+ CloseHandle( d->pipeStdin[0] );
+ if ( comms & Stdout )
+ CloseHandle( d->pipeStdout[1] );
+ if ( (comms & Stderr) && !(comms & DupStderr) )
+ CloseHandle( d->pipeStderr[1] );
+#endif
+
+ if ( ioRedirection || notifyOnExit ) {
+ d->lookup->start( 100 );
+ }
+
+ // cleanup and return
+ return true;
+}
+
+static BOOL QT_WIN_CALLBACK qt_terminateApp( HWND hwnd, LPARAM procId )
+{
+ DWORD procId_win;
+ GetWindowThreadProcessId( hwnd, &procId_win );
+ if( procId_win == (DWORD)procId )
+ PostMessage( hwnd, WM_CLOSE, 0, 0 );
+
+ return TRUE;
+}
+
+void Q3Process::tryTerminate() const
+{
+ if ( d->pid )
+ EnumWindows( qt_terminateApp, (LPARAM)d->pid->dwProcessId );
+}
+
+void Q3Process::kill() const
+{
+ if ( d->pid )
+ TerminateProcess( d->pid->hProcess, 0xf291 );
+}
+
+bool Q3Process::isRunning() const
+{
+ if ( !d->pid )
+ return false;
+
+ if ( WaitForSingleObject( d->pid->hProcess, 0) == WAIT_OBJECT_0 ) {
+ // there might be data to read
+ Q3Process *that = (Q3Process*)this;
+ that->socketRead( 1 ); // try stdout
+ that->socketRead( 2 ); // try stderr
+ // compute the exit values
+ if ( !d->exitValuesCalculated ) {
+ DWORD exitCode;
+ if ( GetExitCodeProcess( d->pid->hProcess, &exitCode ) ) {
+ if ( exitCode != STILL_ACTIVE ) { // this should ever be true?
+ that->exitNormal = exitCode != 0xf291;
+ that->exitStat = exitCode;
+ }
+ }
+ d->exitValuesCalculated = true;
+ }
+ d->deletePid();
+ d->closeHandles();
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool Q3Process::canReadLineStdout() const
+{
+ if( !d->pipeStdout[0] )
+ return d->bufStdout.size() != 0;
+
+ Q3Process *that = (Q3Process*)this;
+ return that->membufStdout()->scanNewline( 0 );
+}
+
+bool Q3Process::canReadLineStderr() const
+{
+ if( !d->pipeStderr[0] )
+ return d->bufStderr.size() != 0;
+
+ Q3Process *that = (Q3Process*)this;
+ return that->membufStderr()->scanNewline( 0 );
+}
+
+void Q3Process::writeToStdin( const QByteArray& buf )
+{
+ d->stdinBuf.enqueue( new QByteArray(buf) );
+ socketWrite( 0 );
+}
+
+void Q3Process::closeStdin( )
+{
+ if ( d->pipeStdin[1] != 0 ) {
+ CloseHandle( d->pipeStdin[1] );
+ d->pipeStdin[1] = 0;
+ }
+}
+
+void Q3Process::socketRead( int fd )
+{
+ // fd == 1: stdout, fd == 2: stderr
+ HANDLE dev;
+ if ( fd == 1 ) {
+ dev = d->pipeStdout[0];
+ } else if ( fd == 2 ) {
+ dev = d->pipeStderr[0];
+ } else {
+ return;
+ }
+#ifndef Q_OS_WINCE
+ // get the number of bytes that are waiting to be read
+ unsigned long i, r;
+ char dummy;
+ if ( !PeekNamedPipe( dev, &dummy, 1, &r, &i, 0 ) ) {
+ return; // ### is it worth to dig for the reason of the error?
+ }
+#else
+ unsigned long i = 1000;
+#endif
+ if ( i > 0 ) {
+ Q3Membuf *buffer;
+ if ( fd == 1 )
+ buffer = &d->bufStdout;
+ else
+ buffer = &d->bufStderr;
+
+ QByteArray *ba = new QByteArray( i );
+ uint sz = readStddev( dev, ba->data(), i );
+ if ( sz != i )
+ ba->resize( i );
+
+ if ( sz == 0 ) {
+ delete ba;
+ return;
+ }
+ buffer->append( ba );
+ if ( fd == 1 )
+ emit readyReadStdout();
+ else
+ emit readyReadStderr();
+ }
+}
+
+void Q3Process::socketWrite( int )
+{
+ DWORD written;
+ while ( !d->stdinBuf.isEmpty() && isRunning() ) {
+ if ( !WriteFile( d->pipeStdin[1],
+ d->stdinBuf.head()->data() + d->stdinBufRead,
+ qMin( 8192, int(d->stdinBuf.head()->size() - d->stdinBufRead) ),
+ &written, 0 ) ) {
+ d->lookup->start( 100 );
+ return;
+ }
+ d->stdinBufRead += written;
+ if ( d->stdinBufRead == (DWORD)d->stdinBuf.head()->size() ) {
+ d->stdinBufRead = 0;
+ delete d->stdinBuf.dequeue();
+ if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
+ emit wroteToStdin();
+ }
+ }
+}
+
+void Q3Process::flushStdin()
+{
+ socketWrite( 0 );
+}
+
+/*
+ Use a timer for polling misc. stuff.
+*/
+void Q3Process::timeout()
+{
+ // Disable the timer temporary since one of the slots that are connected to
+ // the readyRead...(), etc. signals might trigger recursion if
+ // processEvents() is called.
+ d->lookup->stop();
+
+ // try to write pending data to stdin
+ if ( !d->stdinBuf.isEmpty() )
+ socketWrite( 0 );
+
+ if ( ioRedirection ) {
+ socketRead( 1 ); // try stdout
+ socketRead( 2 ); // try stderr
+ }
+
+ if ( isRunning() ) {
+ // enable timer again, if needed
+ if ( !d->stdinBuf.isEmpty() || ioRedirection || notifyOnExit )
+ d->lookup->start( 100 );
+ } else if ( notifyOnExit ) {
+ emit processExited();
+ }
+}
+
+/*
+ read on the pipe
+*/
+uint Q3Process::readStddev( HANDLE dev, char *buf, uint bytes )
+{
+ if ( bytes > 0 ) {
+ ulong r;
+ if ( ReadFile( dev, buf, bytes, &r, 0 ) )
+ return r;
+ }
+ return 0;
+}
+
+/*
+ Used by connectNotify() and disconnectNotify() to change the value of
+ ioRedirection (and related behaviour)
+*/
+void Q3Process::setIoRedirection( bool value )
+{
+ ioRedirection = value;
+ if ( !ioRedirection && !notifyOnExit )
+ d->lookup->stop();
+ if ( ioRedirection ) {
+ if ( isRunning() )
+ d->lookup->start( 100 );
+ }
+}
+
+/*
+ Used by connectNotify() and disconnectNotify() to change the value of
+ notifyOnExit (and related behaviour)
+*/
+void Q3Process::setNotifyOnExit( bool value )
+{
+ notifyOnExit = value;
+ if ( !ioRedirection && !notifyOnExit )
+ d->lookup->stop();
+ if ( notifyOnExit ) {
+ if ( isRunning() )
+ d->lookup->start( 100 );
+ }
+}
+
+/*
+ Used by connectNotify() and disconnectNotify() to change the value of
+ wroteToStdinConnected (and related behaviour)
+*/
+void Q3Process::setWroteStdinConnected( bool value )
+{
+ wroteToStdinConnected = value;
+}
+
+Q3Process::PID Q3Process::processIdentifier()
+{
+ return d->pid;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROCESS
diff --git a/src/qt3support/other/qiconset.h b/src/qt3support/other/qiconset.h
new file mode 100644
index 0000000..9f7c338
--- /dev/null
+++ b/src/qt3support/other/qiconset.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qicon.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/other/qt_compat_pch.h b/src/qt3support/other/qt_compat_pch.h
new file mode 100644
index 0000000..abd8409
--- /dev/null
+++ b/src/qt3support/other/qt_compat_pch.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ * This is a precompiled header file for use in Xcode / Mac GCC /
+ * GCC >= 3.4 / VC to greatly speed the building of Qt. It may also be
+ * of use to people developing their own project, but it is probably
+ * better to define your own header. Use of this header is currently
+ * UNSUPPORTED.
+ */
+
+#if defined __cplusplus
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qdesktopwidget.h>
+#include <qevent.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qstyle.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+#include <stdlib.h>
+#endif
diff --git a/src/qt3support/painting/painting.pri b/src/qt3support/painting/painting.pri
new file mode 100644
index 0000000..f368de5
--- /dev/null
+++ b/src/qt3support/painting/painting.pri
@@ -0,0 +1,15 @@
+SOURCES += \
+ painting/q3paintdevicemetrics.cpp \
+ painting/q3pointarray.cpp \
+ painting/q3painter.cpp \
+ painting/q3picture.cpp \
+ painting/q3paintengine_svg.cpp
+
+HEADERS += \
+ painting/q3paintdevicemetrics.h \
+ painting/q3pointarray.h \
+ painting/q3painter.h \
+ painting/q3picture.h \
+ painting/q3paintengine_svg_p.h
+
+QT += xml
diff --git a/src/qt3support/painting/q3paintdevicemetrics.cpp b/src/qt3support/painting/q3paintdevicemetrics.cpp
new file mode 100644
index 0000000..67546a5
--- /dev/null
+++ b/src/qt3support/painting/q3paintdevicemetrics.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3paintdevicemetrics.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3PaintDeviceMetrics
+ \brief The Q3PaintDeviceMetrics class provides information about a
+ paint device.
+
+ \compat
+
+ Sometimes when drawing graphics it is necessary to obtain
+ information about the physical characteristics of a paint device.
+ This class provides the information. For example, to compute the
+ aspect ratio of a paint device:
+
+ \snippet doc/src/snippets/code/src_qt3support_painting_q3paintdevicemetrics.cpp 0
+
+ Q3PaintDeviceMetrics contains methods to provide the width and
+ height of a device in both pixels (width() and height()) and
+ millimeters (widthMM() and heightMM()), the number of colors the
+ device supports (numColors()), the number of bit planes (depth()),
+ and the resolution of the device (logicalDpiX() and
+ logicalDpiY()).
+
+ It is not always possible for Q3PaintDeviceMetrics to compute the
+ values you ask for, particularly for external devices. The
+ ultimate example is asking for the resolution of of a QPrinter
+ that is set to "print to file": who knows what printer that file
+ will end up on?
+*/
+
+/*!
+ \fn Q3PaintDeviceMetrics::Q3PaintDeviceMetrics(const QPaintDevice *pd)
+
+ Constructs a metric for the paint device \a pd.
+*/
+
+
+/*!
+ \fn int Q3PaintDeviceMetrics::width() const
+
+ Returns the width of the paint device in default coordinate system
+ units (e.g. pixels for QPixmap and QWidget).
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::height() const
+
+ Returns the height of the paint device in default coordinate
+ system units (e.g. pixels for QPixmap and QWidget).
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::widthMM() const
+
+ Returns the width of the paint device, measured in millimeters.
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::heightMM() const
+
+ Returns the height of the paint device, measured in millimeters.
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::numColors() const
+
+ Returns the number of different colors available for the paint
+ device. Since this value is an int will not be sufficient to represent
+ the number of colors on 32 bit displays, in which case INT_MAX is
+ returned instead.
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::depth() const
+
+ Returns the bit depth (number of bit planes) of the paint device.
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::logicalDpiX() const
+
+ Returns the horizontal resolution of the device in dots per inch,
+ which is used when computing font sizes. For X, this is usually
+ the same as could be computed from widthMM(), but it varies on
+ Windows.
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::logicalDpiY() const
+
+ Returns the vertical resolution of the device in dots per inch,
+ which is used when computing font sizes. For X, this is usually
+ the same as could be computed from heightMM(), but it varies on
+ Windows.
+*/
+
+/*!
+ \fn int Q3PaintDeviceMetrics::physicalDpiX() const
+ \internal
+*/
+/*!
+ \fn int Q3PaintDeviceMetrics::physicalDpiY() const
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/painting/q3paintdevicemetrics.h b/src/qt3support/painting/q3paintdevicemetrics.h
new file mode 100644
index 0000000..51c2aec
--- /dev/null
+++ b/src/qt3support/painting/q3paintdevicemetrics.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PAINTDEVICEMETRICS_H
+#define Q3PAINTDEVICEMETRICS_H
+
+#include <QtGui/qpaintdevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3PaintDeviceMetrics // paint device metrics
+{
+public:
+ Q3PaintDeviceMetrics(const QPaintDevice *device) : pdev(device) {}
+
+ int width() const { return pdev->width(); }
+ int height() const { return pdev->height(); }
+ int widthMM() const { return pdev->widthMM(); }
+ int heightMM() const { return pdev->heightMM(); }
+ int logicalDpiX() const { return pdev->logicalDpiX(); }
+ int logicalDpiY() const { return pdev->logicalDpiY(); }
+ int physicalDpiX() const { return pdev->physicalDpiX(); }
+ int physicalDpiY() const { return pdev->physicalDpiY(); }
+ int numColors() const { return pdev->colorCount(); }
+ int depth() const { return pdev->depth(); }
+
+private:
+ const QPaintDevice *pdev;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PAINTDEVICEMETRICS_H
diff --git a/src/qt3support/painting/q3paintengine_svg.cpp b/src/qt3support/painting/q3paintengine_svg.cpp
new file mode 100644
index 0000000..b60b53b
--- /dev/null
+++ b/src/qt3support/painting/q3paintengine_svg.cpp
@@ -0,0 +1,1538 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qpainter_p.h>
+#include <private/qpaintengine_p.h>
+#include "qfile.h"
+#include "qimage.h"
+#include "qlist.h"
+#include "qmap.h"
+#include "q3paintengine_svg_p.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qregexp.h"
+#include "qtextstream.h"
+
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+static const double deg2rad = 0.017453292519943295769; // pi/180
+static const char piData[] = "version=\"1.0\" standalone=\"no\"";
+static const char publicId[] = "-//W3C//DTD SVG 20001102//EN";
+static const char systemId[] = "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd";
+
+static QString qt_svg_compose_path(const QPainterPath &path);
+
+struct QImgElement {
+ QDomElement element;
+ QImage image;
+ Q_DUMMY_COMPARISON_OPERATOR(QImgElement)
+};
+
+struct QPixElement {
+ QDomElement element;
+ QPixmap pixmap;
+ Q_DUMMY_COMPARISON_OPERATOR(QPixElement)
+};
+
+struct Q3SVGPaintEngineState {
+ double textx, texty; // current text position
+ int textalign; // text alignment
+ Q_DUMMY_COMPARISON_OPERATOR(Q3SVGPaintEngineState)
+};
+
+typedef QList<QImgElement> ImageList;
+typedef QList<QPixElement> PixmapList;
+typedef QList<Q3SVGPaintEngineState> StateList;
+
+enum ElementType {
+ InvalidElement = 0,
+ AnchorElement,
+ CircleElement,
+ ClipElement,
+ CommentElement,
+ DescElement,
+ EllipseElement,
+ GroupElement,
+ ImageElement,
+ LineElement,
+ PolylineElement,
+ PolygonElement,
+ PathElement,
+ RectElement,
+ SvgElement,
+ TextElement,
+ TitleElement,
+ TSpanElement
+};
+
+typedef QMap<QString,ElementType> QSvgTypeMap;
+static QSvgTypeMap *qSvgTypeMap=0; // element types
+static QMap<QString,QString> *qSvgColMap=0; // recognized color keyword names
+
+class Q3SVGPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(Q3SVGPaintEngine)
+
+public:
+ Q3SVGPaintEnginePrivate()
+ : dirtyTransform(false), dirtyStyle(false), currentClip(0),
+ dev(0), wwidth(0), wheight(0) {}
+ void appendChild(QDomElement &e, QPicturePrivate::PaintCommand c);
+ void applyStyle(QDomElement *e, QPicturePrivate::PaintCommand c) const;
+ void applyTransform(QDomElement *e) const;
+ double parseLen(const QString &str, bool *ok=0, bool horiz=true) const;
+ int lenToInt(const QDomNamedNodeMap &map, const QString &attr, int def = 0) const;
+ double lenToDouble(const QDomNamedNodeMap &map, const QString &attr, int def = 0) const;
+ bool play(const QDomNode &node, QPainter *p);
+ void setTransform(const QString &tr, QPainter *p);
+ void restoreAttributes(QPainter *p);
+ void saveAttributes(QPainter *p);
+ void setStyle(const QString &s, QPainter *p);
+ void setStyleProperty(const QString &prop, const QString &val, QPen *pen, QFont *font,
+ int *talign, QPainter *p);
+ void drawPath(const QString &data, QPainter *p);
+ QColor parseColor(const QString &col);
+ void init() {
+ QDomImplementation domImpl;
+ QDomDocumentType docType = domImpl.createDocumentType(QLatin1String("svg"), QLatin1String(publicId), QLatin1String(systemId));
+ doc = domImpl.createDocument(QLatin1String("http://www.w3.org/2000/svg"), QLatin1String("svg"), docType);
+ doc.insertBefore(doc.createProcessingInstruction(QLatin1String("xml"), QLatin1String(piData)), doc.firstChild());
+ current = doc.documentElement();
+ images.clear();
+ pixmaps.clear();
+
+ doc.documentElement().setAttribute(QLatin1String("xmlns:xlink"), QLatin1String("http://www.w3.org/1999/xlink"));
+ }
+
+
+ bool dirtyTransform;
+ bool dirtyStyle;
+ QRect brect; // bounding rectangle
+ QDomDocument doc; // document tree
+ QDomNode current;
+
+ ImageList images; // old private
+ PixmapList pixmaps;
+ StateList stack;
+ int currentClip;
+
+// QPoint curPt;
+ Q3SVGPaintEngineState *curr;
+// QPainter *pt; // only used by recursive play() et al
+ QPen cpen;
+ QBrush cbrush;
+ QFont cfont;
+ QMatrix worldMatrix;
+ const QPaintDevice *dev;
+ int wwidth;
+ int wheight;
+};
+
+Q3SVGPaintEngine::Q3SVGPaintEngine()
+ : QPaintEngine(*(new Q3SVGPaintEnginePrivate), AllFeatures)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->init();
+}
+
+Q3SVGPaintEngine::Q3SVGPaintEngine(Q3SVGPaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, AllFeatures)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->init();
+}
+
+Q3SVGPaintEngine::~Q3SVGPaintEngine()
+{
+ delete qSvgTypeMap; qSvgTypeMap = 0; // static
+ delete qSvgColMap; qSvgColMap = 0;
+}
+
+bool Q3SVGPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->dirtyTransform = d->dirtyStyle = false;
+ d->dev = pdev;
+ setActive(true);
+ return true;
+}
+
+bool Q3SVGPaintEngine::end()
+{
+ Q_D(Q3SVGPaintEngine);
+ d->dev = 0;
+ setActive(false);
+ return true;
+}
+
+void Q3SVGPaintEngine::updateState(const QPaintEngineState &state)
+{
+ QPaintEngine::DirtyFlags flags = state.state();
+ if (flags & DirtyPen) updatePen(state.pen());
+ if ((flags & DirtyBrush) || (flags & DirtyBrushOrigin))
+ updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyBackground) updateBackground(state.backgroundMode(), state.backgroundBrush());
+ if (flags & DirtyFont) updateFont(state.font());
+ if (flags & DirtyTransform) updateMatrix(state.matrix());
+ if (flags & DirtyClipRegion) updateClipRegion(state.clipRegion(), state.clipOperation());
+ if (flags & DirtyClipPath) updateClipPath(state.clipPath(), state.clipOperation());
+}
+
+void Q3SVGPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->cpen = pen;
+ d->dirtyStyle = true;
+}
+
+void Q3SVGPaintEngine::updateBrush(const QBrush &brush, const QPointF &)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->cbrush = brush;
+ d->dirtyStyle = true;
+}
+
+void Q3SVGPaintEngine::updateFont(const QFont &font)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->cfont = font;
+ d->dirtyStyle = true;
+}
+
+void Q3SVGPaintEngine::updateBackground(Qt::BGMode, const QBrush &)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->dirtyStyle = true;
+}
+
+void Q3SVGPaintEngine::updateMatrix(const QMatrix &matrix)
+{
+ Q_D(Q3SVGPaintEngine);
+ d->dirtyTransform = true;
+ d->worldMatrix = matrix;
+// d->wwidth = ps->ww;
+// d->wheight = ps->wh;
+}
+
+void Q3SVGPaintEngine::updateClipPath(const QPainterPath &path, Qt::ClipOperation op)
+{
+ Q_D(Q3SVGPaintEngine);
+ if (op == Qt::NoClip)
+ return;
+
+ QDomElement e;
+ d->currentClip++;
+ e = d->doc.createElement(QLatin1String("clipPath"));
+ e.setAttribute(QLatin1String("id"), QString::fromLatin1("clip%1").arg(d->currentClip));
+
+ QDomElement path_element = d->doc.createElement(QLatin1String("path"));
+ path_element.setAttribute(QLatin1String("d"), qt_svg_compose_path(path));
+ e.appendChild(path_element);
+
+ d->appendChild(e, QPicturePrivate::PdcSetClipPath);
+}
+
+void Q3SVGPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ QPainterPath clipPath;
+ clipPath.addRegion(clipRegion);
+ updateClipPath(clipPath, op);
+}
+
+void Q3SVGPaintEngine::updateRenderHints(QPainter::RenderHints)
+{
+}
+
+void Q3SVGPaintEngine::drawRect(const QRectF &r)
+{
+ Q_D(Q3SVGPaintEngine);
+ QDomElement e;
+ e = d->doc.createElement(QLatin1String("rect"));
+
+ e.setAttribute(QLatin1String("x"), r.x());
+ e.setAttribute(QLatin1String("y"), r.y());
+ e.setAttribute(QLatin1String("width"), r.width());
+ e.setAttribute(QLatin1String("height"), r.height());
+ d->appendChild(e, QPicturePrivate::PdcDrawRect);
+}
+
+void Q3SVGPaintEngine::drawPoint(const QPointF &p)
+{
+ QLineF l(p, p);
+ drawLines(&l, 1);
+}
+
+void Q3SVGPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ for (int i = 0; i < pointCount; ++i) {
+ QLineF l(points[i], points[i]);
+ drawLines(&l, 1);
+ }
+}
+
+void Q3SVGPaintEngine::drawEllipse(const QRect &r)
+{
+ Q_D(Q3SVGPaintEngine);
+ QDomElement e;
+
+ if (r.width() == r.height()) {
+ e = d->doc.createElement(QLatin1String("circle"));
+ double cx = r.x() + (r.width() / 2.0);
+ double cy = r.y() + (r.height() / 2.0);
+ e.setAttribute(QLatin1String("cx"), cx);
+ e.setAttribute(QLatin1String("cy"), cy);
+ e.setAttribute(QLatin1String("r"), cx - r.x());
+ } else {
+ e = d->doc.createElement(QLatin1String("ellipse"));
+ double cx = r.x() + (r.width() / 2.0);
+ double cy = r.y() + (r.height() / 2.0);
+ e.setAttribute(QLatin1String("cx"), cx);
+ e.setAttribute(QLatin1String("cy"), cy);
+ e.setAttribute(QLatin1String("rx"), cx - r.x());
+ e.setAttribute(QLatin1String("ry"), cy - r.y());
+ }
+ d->appendChild(e, QPicturePrivate::PdcDrawEllipse);
+}
+
+void Q3SVGPaintEngine::drawLine(const QLineF &line)
+{
+ drawLines(&line, 1);
+}
+
+void Q3SVGPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(Q3SVGPaintEngine);
+ QDomElement e;
+
+ for (int i = 0; i < lineCount; ++i) {
+ e = d->doc.createElement(QLatin1String("line"));
+ e.setAttribute(QLatin1String("x1"), lines[i].x1());
+ e.setAttribute(QLatin1String("y1"), lines[i].y1());
+ e.setAttribute(QLatin1String("x2"), lines[i].x2());
+ e.setAttribute(QLatin1String("y2"), lines[i].y2());
+ d->appendChild(e, QPicturePrivate::PdcDrawLineSegments);
+ }
+}
+
+void Q3SVGPaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(Q3SVGPaintEngine);
+ QDomElement e = d->doc.createElement(QLatin1String("path"));
+ e.setAttribute(QLatin1String("d"), qt_svg_compose_path(path));
+ d->appendChild(e, QPicturePrivate::PdcDrawPath);
+}
+
+void Q3SVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(Q3SVGPaintEngine);
+ QString str;
+ if (mode == PolylineMode) {
+ QDomElement e = d->doc.createElement(QLatin1String("polyline"));
+ for (int i = 0; i < pointCount; ++i) {
+ QString tmp;
+ tmp.sprintf("%f %f ", points[i].x(), points[i].y());
+ str += tmp;
+ }
+ e.setAttribute(QLatin1String("points"), str.trimmed());
+ d->appendChild(e, QPicturePrivate::PdcDrawPolyline);
+ } else {
+ QDomElement e = d->doc.createElement(QLatin1String("polygon"));
+ for (int i = 0; i < pointCount; ++i) {
+ QString tmp;
+ tmp.sprintf("%f %f ", points[i].x(), points[i].y());
+ str += tmp;
+ }
+ e.setAttribute(QLatin1String("points"), str.trimmed());
+ d->appendChild(e, QPicturePrivate::PdcDrawPolygon);
+ }
+}
+
+void Q3SVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ QPolygonF poly;
+ for (int i = 0; i < pointCount; ++i)
+ poly << points[i];
+ drawPolygon(poly.constData(), pointCount, mode);
+}
+
+void Q3SVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF & /* sr */)
+{
+ Q_D(Q3SVGPaintEngine);
+ QDomElement e = d->doc.createElement(QLatin1String("image"));
+ e.setAttribute(QLatin1String("x"), r.x());
+ e.setAttribute(QLatin1String("y"), r.y());
+ e.setAttribute(QLatin1String("width"), r.width());
+ e.setAttribute(QLatin1String("height"), r.height());
+
+ QPixElement pe;
+ pe.element = e;
+ pe.pixmap = pm;
+ d->pixmaps.append(pe);
+
+ // saving to disk and setting the xlink:href attribute will be
+ // done later in save() once we now the svg document name.
+ d->appendChild(e, QPicturePrivate::PdcDrawPixmap);
+}
+
+void Q3SVGPaintEngine::drawTiledPixmap(const QRectF & /* r */, const QPixmap & /* pixmap */,
+ const QPointF & /* s */)
+{
+}
+
+void Q3SVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
+{
+ Q_D(Q3SVGPaintEngine);
+ QDomElement e = d->doc.createElement(QLatin1String("text"));
+// int x, y;
+// const QRect r(p.x(), p.y(), ti.width, ti.ascent + ti.descent);
+ // horizontal text alignment
+ // if ((ti.flags & Qt::AlignHCenter) != 0) {
+// x = r.x() + r.width() / 2;
+// e.setAttribute("text-anchor", "middle");
+// } else if ((textflags & Qt::AlignRight) != 0) {
+// x = r.right();
+// e.setAttribute("text-anchor", "end");
+// } else {
+// x = r.x();
+// }
+// // vertical text alignment
+// if ((textflags & Qt::AlignVCenter) != 0)
+// y = r.y() + (r.height() + ti.ascent) / 2;
+// else if ((textflags & Qt::AlignBottom) != 0)
+// y = r.bottom();
+// else
+// y = r.y() + ti.ascent;
+// if (x)
+// e.setAttribute("x", x);
+// if (y)
+// e.setAttribute("y", y);
+ e.setAttribute(QLatin1String("x"), p.x());
+ e.setAttribute(QLatin1String("y"), p.y());
+ e.appendChild(d->doc.createTextNode(ti.text()));
+}
+
+void Q3SVGPaintEngine::drawImage(const QRectF &r, const QImage &im,
+ const QRectF &, Qt::ImageConversionFlags)
+{
+ Q_D(Q3SVGPaintEngine);
+ QDomElement e = d->doc.createElement(QLatin1String("image"));
+ e.setAttribute(QLatin1String("x"), r.x());
+ e.setAttribute(QLatin1String("y"), r.y());
+ e.setAttribute(QLatin1String("width"), r.width());
+ e.setAttribute(QLatin1String("height"), r.height());
+ QImgElement ie;
+ ie.element = e;
+ ie.image = im;
+ d->images.append(ie);
+ // saving to disk and setting the xlink:href attribute will be
+ // done later in save() once we now the svg document name.
+ d->appendChild(e, QPicturePrivate::PdcDrawImage);
+}
+
+
+/*!
+ Returns the SVG as a single string of XML.
+*/
+
+QString Q3SVGPaintEngine::toString() const
+{
+ Q_D(const Q3SVGPaintEngine);
+ if (d->doc.isNull())
+ return QString();
+
+ return d->doc.toString();
+}
+
+/*!
+ Saves the SVG to \a fileName.
+*/
+
+bool Q3SVGPaintEngine::save(const QString &fileName)
+{
+ Q_D(Q3SVGPaintEngine);
+ // guess svg id from fileName
+ QString svgName = fileName.endsWith(QLatin1String(".svg")) ?
+ fileName.left(fileName.length()-4) : fileName;
+
+ // now we have the info about name and dimensions available
+ QDomElement root = d->doc.documentElement();
+ root.setAttribute(QLatin1String("id"), svgName);
+ // the standard doesn't take respect x and y. But we want a
+ // proper bounding rect. We make width and height bigger when
+ // writing out and subtract x and y when reading in.
+ root.setAttribute(QLatin1String("x"), d->brect.x());
+ root.setAttribute(QLatin1String("y"), d->brect.y());
+ root.setAttribute(QLatin1String("width"), d->brect.width() + d->brect.x());
+ root.setAttribute(QLatin1String("height"), d->brect.height() + d->brect.y());
+
+ // ... and know how to name any image files to be written out
+ int icount = 0;
+ ImageList::Iterator iit = d->images.begin();
+ for (; iit != d->images.end(); ++iit) {
+ QString href = QString::fromLatin1("%1_%2.png").arg(svgName).arg(icount);
+ (*iit).image.save(href, "PNG");
+ (*iit).element.setAttribute(QLatin1String("xlink:href"), href);
+ icount++;
+ }
+ PixmapList::Iterator pit = d->pixmaps.begin();
+ for (; pit != d->pixmaps.end(); ++pit) {
+ QString href = QString::fromLatin1("%1_%2.png").arg(svgName).arg(icount);
+ (*pit).pixmap.save(href, "PNG");
+ (*pit).element.setAttribute(QLatin1String("xlink:href"), href);
+ icount++;
+ }
+
+ QFile f(fileName);
+ if (!f.open (QIODevice::WriteOnly))
+ return false;
+ QTextStream s(&f);
+ s.setEncoding(QTextStream::UnicodeUTF8);
+ s << d->doc;
+
+ return true;
+}
+
+/*!
+ \overload
+
+ \a dev is the device to use for saving.
+*/
+
+bool Q3SVGPaintEngine::save(QIODevice *dev)
+{
+ Q_D(Q3SVGPaintEngine);
+#if defined(CHECK_RANGE)
+ if (!d->images.isEmpty() || !d->pixmaps.isEmpty())
+ qWarning("Q3SVGPaintEngine::save: skipping external images");
+#endif
+
+ QTextStream s(dev);
+ s.setEncoding(QTextStream::UnicodeUTF8);
+ s << d->doc;
+
+ return true;
+}
+
+/*!
+ Sets the bounding rectangle of the SVG to rectangle \a r.
+*/
+
+void Q3SVGPaintEngine::setBoundingRect(const QRect &r)
+{
+ d_func()->brect = r;
+}
+
+/*!
+ Returns the SVG's bounding rectangle.
+*/
+
+QRect Q3SVGPaintEngine::boundingRect() const
+{
+ return d_func()->brect;
+}
+
+/*!
+ Loads and parses a SVG from \a dev into the device. Returns true
+ on success (i.e. loaded and parsed without error); otherwise
+ returns false.
+*/
+
+bool Q3SVGPaintEngine::load(QIODevice *dev)
+{
+ return d_func()->doc.setContent(dev);
+}
+
+void Q3SVGPaintEnginePrivate::appendChild(QDomElement &e, QPicturePrivate::PaintCommand c)
+{
+ if (!e.isNull()) {
+ current.appendChild(e);
+ if (c == QPicturePrivate::PdcSave)
+ current = e;
+ // ### optimize application of attributes utilizing <g>
+ if (c == QPicturePrivate::PdcSetClipRegion || c == QPicturePrivate::PdcSetClipPath) {
+ QDomElement ne;
+ ne = doc.createElement(QLatin1String("g"));
+ ne.setAttribute(QLatin1String("style"), QString::fromLatin1("clip-path:url(#clip%1)").arg(currentClip));
+ if (dirtyTransform) {
+ applyTransform(&ne);
+ dirtyTransform = false;
+ }
+ current.appendChild(ne);
+ current = ne;
+ } else {
+ if (dirtyStyle) // only reset when entering
+ applyStyle(&e, c); // or leaving a <g> tag
+ if (dirtyTransform && e.tagName() != QLatin1String("g")) {
+ // same as above but not for <g> tags
+ applyTransform(&e);
+ if (c == QPicturePrivate::PdcSave)
+ dirtyTransform = false;
+ }
+ }
+ }
+}
+
+void Q3SVGPaintEnginePrivate::applyStyle(QDomElement *e, QPicturePrivate::PaintCommand c) const
+{
+ // ### do not write every attribute each time
+ QColor pcol = cpen.color();
+ QColor bcol = cbrush.color();
+ QString s;
+ if (c == QPicturePrivate::PdcDrawText2 || c == QPicturePrivate::PdcDrawText2Formatted) {
+ // QPainter has a reversed understanding of pen/stroke vs.
+ // brush/fill for text
+ s += QString::fromLatin1("fill:rgb(%1,%2,%3);").arg(pcol.red()).arg(pcol.green()).arg(pcol.blue());
+ s += QLatin1String("stroke-width:0;");
+ QFont f = cfont;
+ QFontInfo fi(f);
+ s += QString::fromLatin1("font-size:%1;").arg(fi.pointSize());
+ s += QString::fromLatin1("font-style:%1;").arg(f.italic() ? QLatin1String("italic") : QLatin1String("normal"));
+ // not a very scientific distribution
+ QString fw;
+ if (f.weight() <= QFont::Light)
+ fw = QLatin1String("100");
+ else if (f.weight() <= QFont::Normal)
+ fw = QLatin1String("400");
+ else if (f.weight() <= QFont::DemiBold)
+ fw = QLatin1String("600");
+ else if (f.weight() <= QFont::Bold)
+ fw = QLatin1String("700");
+ else if (f.weight() <= QFont::Black)
+ fw = QLatin1String("800");
+ else
+ fw = QLatin1String("900");
+ s += QString::fromLatin1("font-weight:%1;").arg(fw);
+ s += QString::fromLatin1("font-family:%1;").arg(f.family());
+ } else {
+ s += QString::fromLatin1("stroke:rgb(%1,%2,%3);").arg(pcol.red()).arg(pcol.green()).arg(pcol.blue());
+ if (pcol.alpha() != 255)
+ s += QString::fromLatin1("stroke-opacity:%1;").arg(pcol.alpha()/255.0);
+ if (bcol.alpha() != 255)
+ s += QString::fromLatin1("fill-opacity:%1;").arg(bcol.alpha()/255.0);
+ double pw = cpen.width();
+ if (pw == 0 && cpen.style() != Qt::NoPen)
+ pw = 0.9;
+ if (c == QPicturePrivate::PdcDrawLine)
+ pw /= (qAbs(worldMatrix.m11()) + qAbs(worldMatrix.m22())) / 2.0;
+ s += QString::fromLatin1("stroke-width:%1;").arg(pw);
+ if (cpen.style() == Qt::DashLine)
+ s+= QLatin1String("stroke-dasharray:18,6;");
+ else if (cpen.style() == Qt::DotLine)
+ s+= QLatin1String("stroke-dasharray:3;");
+ else if (cpen.style() == Qt::DashDotLine)
+ s+= QLatin1String("stroke-dasharray:9,6,3,6;");
+ else if (cpen.style() == Qt::DashDotDotLine)
+ s+= QLatin1String("stroke-dasharray:9,3,3;");
+ if (cbrush.style() == Qt::NoBrush || c == QPicturePrivate::PdcDrawPolyline || c == QPicturePrivate::PdcDrawCubicBezier)
+ s += QLatin1String("fill:none;"); // Qt polylines use no brush, neither do Beziers
+ else
+ s += QString::fromLatin1("fill:rgb(%1,%2,%3);").arg(bcol.red()).arg(bcol.green()).arg(bcol.blue());
+ }
+ e->setAttribute(QLatin1String("style"), s);
+}
+
+void Q3SVGPaintEnginePrivate::applyTransform(QDomElement *e) const
+{
+ QMatrix m = worldMatrix;
+
+ QString s;
+ bool rot = (m.m11() != 1.0 || m.m12() != 0.0 ||
+ m.m21() != 0.0 || m.m22() != 1.0);
+ if (!rot && (m.dx() != 0.0 || m.dy() != 0.0)) {
+ s = QString::fromLatin1("translate(%1,%2)").arg(m.dx()).arg(m.dy());
+ } else if (rot) {
+ if (m.m12() == 0.0 && m.m21() == 0.0 &&
+ m.dx() == 0.0 && m.dy() == 0.0)
+ s = QString::fromLatin1("scale(%1,%2)").arg(m.m11()).arg(m.m22());
+ else
+ s = QString::fromLatin1("matrix(%1,%2,%3,%4,%5,%6)")
+ .arg(m.m11()).arg(m.m12())
+ .arg(m.m21()).arg(m.m22())
+ .arg(m.dx()).arg(m.dy());
+ } else {
+ return;
+ }
+ e->setAttribute(QLatin1String("transform"), s);
+}
+
+bool Q3SVGPaintEngine::play(QPainter *pt)
+{
+ Q_D(Q3SVGPaintEngine);
+ if (!pt) {
+ Q_ASSERT(pt);
+ return false;
+ }
+ if (d->dev == 0)
+ d->dev = pt->device();
+ d->wwidth = pt->window().width();
+ d->wheight = pt->window().height();
+
+ pt->setPen(Qt::NoPen); // SVG default pen and brush
+ pt->setBrush(Qt::black);
+ if (d->doc.isNull()) {
+ qWarning("Q3SVGPaintEngine::play: No SVG data set.");
+ return false;
+ }
+
+ QDomNode svg = d->doc.namedItem(QLatin1String("svg"));
+ if (svg.isNull() || !svg.isElement()) {
+ qWarning("Q3SVGPaintEngine::play: Couldn't find any svg element.");
+ return false;
+ }
+
+ // force transform to be activated in case our sequences
+ // are replayed later with a transformed painter
+ pt->setWorldXForm(true);
+
+ QDomNamedNodeMap attr = svg.attributes();
+ int x = d->lenToInt(attr, QLatin1String("x"));
+ int y = d->lenToInt(attr, QLatin1String("y"));
+ d->brect.setX(x);
+ d->brect.setY(y);
+ QString wstr = attr.contains(QLatin1String("width"))
+ ? attr.namedItem(QLatin1String("width")).nodeValue() : QString::fromLatin1("100%");
+ QString hstr = attr.contains(QLatin1String("height"))
+ ? attr.namedItem(QLatin1String("height")).nodeValue() : QString::fromLatin1("100%");
+ double width = d->parseLen(wstr, 0, true);
+ double height = d->parseLen(hstr, 0, false);
+ // SVG doesn't respect x and y. But we want a proper bounding rect.
+ d->brect.setWidth(int(width) - x);
+ d->brect.setHeight(int(height) - y);
+ pt->setClipRect(d->brect);
+
+ if (attr.contains(QLatin1String("viewBox"))) {
+ QRegExp re(QString::fromLatin1("\\s*(\\S+)\\s*,?\\s*(\\S+)\\s*,?"),
+ "\\s*(\\S+)\\s*,?\\s*(\\S+)\\s*");
+ if (re.indexIn(attr.namedItem(QLatin1String("viewBox")).nodeValue()) < 0) {
+ qWarning("Q3SVGPaintEngine::play: Invalid viewBox attribute.");
+ return false;
+ } else {
+ double x = re.cap(1).toDouble();
+ double y = re.cap(2).toDouble();
+ double w = re.cap(3).toDouble();
+ double h = re.cap(4).toDouble();
+ if (w < 0 || h < 0) {
+ qWarning("Q3SVGPaintEngine::play: Invalid viewBox dimension.");
+ return false;
+ } else if (w == 0 || h == 0) {
+ return true;
+ }
+ pt->scale(width/w, height/h);
+ pt->translate(-x, -y);
+ }
+ }
+
+ const struct ElementTable {
+ const char *name;
+ ElementType type;
+ } etab[] = {
+ {"a", AnchorElement },
+ {"#comment", CommentElement },
+ {"circle", CircleElement },
+ {"clipPath", ClipElement },
+ {"desc", DescElement },
+ {"ellipse", EllipseElement },
+ {"g", GroupElement },
+ {"image", ImageElement },
+ {"line", LineElement },
+ {"polyline", PolylineElement},
+ {"polygon", PolygonElement },
+ {"path", PathElement },
+ {"rect", RectElement },
+ {"svg", SvgElement },
+ {"text", TextElement },
+ {"tspan", TSpanElement },
+ {"title", TitleElement },
+ {0, InvalidElement }
+ };
+ // initialize only once
+ if (!qSvgTypeMap) {
+ qSvgTypeMap = new QSvgTypeMap;
+ const ElementTable *t = etab;
+ while (t->name) {
+ qSvgTypeMap->insert(QLatin1String(t->name), t->type);
+ t++;
+ }
+ }
+
+ // initial state
+ Q3SVGPaintEngineState st;
+ st.textx = st.texty = 0;
+ st.textalign = Qt::AlignLeft;
+ d->stack.append(st);
+ d->curr = &d->stack.last();
+ // 'play' all elements recursively starting with 'svg' as root
+ bool b = d->play(svg, pt);
+ d->stack.removeFirst();
+ return b;
+}
+
+bool Q3SVGPaintEnginePrivate::play(const QDomNode &node, QPainter *pt)
+{
+ saveAttributes(pt);
+
+ ElementType t = (*qSvgTypeMap)[node.nodeName()];
+
+ if (t == LineElement && pt->pen().style() == Qt::NoPen) {
+ QPen p = pt->pen();
+ p.setStyle(Qt::SolidLine);
+ pt->setPen(p);
+ }
+ QDomNamedNodeMap attr = node.attributes();
+ if (attr.contains(QLatin1String("style")))
+ setStyle(attr.namedItem(QLatin1String("style")).nodeValue(), pt);
+ // ### might have to exclude more elements from transform
+ if (t != SvgElement && attr.contains(QLatin1String("transform")))
+ setTransform(attr.namedItem(QLatin1String("transform")).nodeValue(), pt);
+ uint i = attr.length();
+ if (i > 0) {
+ QPen pen = pt->pen();
+ QFont font = pt->font();
+ while (i--) {
+ QDomNode n = attr.item(i);
+ QString a = n.nodeName();
+ QString val = n.nodeValue().toLower().trimmed();
+ setStyleProperty(a, val, &pen, &font, &curr->textalign, pt);
+ }
+ pt->setPen(pen);
+ pt->setFont(font);
+ }
+
+ double x1, y1, x2, y2, rx, ry, w, h;
+ double cx1, cy1, crx, cry;
+ switch (t) {
+ case CommentElement:
+ // ignore
+ break;
+ case RectElement:
+ rx = ry = 0;
+ x1 = lenToDouble(attr, QLatin1String("x"));
+ y1 = lenToDouble(attr, QLatin1String("y"));
+ w = lenToDouble(attr, QLatin1String("width"));
+ h = lenToDouble(attr, QLatin1String("height"));
+ if (w == 0 || h == 0) // prevent div by zero below
+ break;
+ x2 = attr.contains(QLatin1String("rx")); // tiny abuse of x2 and y2
+ y2 = attr.contains(QLatin1String("ry"));
+ if (x2)
+ rx = lenToDouble(attr, QLatin1String("rx"));
+ if (y2)
+ ry = lenToDouble(attr, QLatin1String("ry"));
+ if (x2 && !y2)
+ ry = rx;
+ else if (!x2 && y2)
+ rx = ry;
+ rx = 200.0*rx / w;
+ ry = 200.0*ry / h;
+ pt->drawRoundRect(QRectF(x1, y1, w, h), int(rx), int(ry));
+ break;
+ case CircleElement:
+ cx1 = lenToDouble(attr, QLatin1String("cx")) + 0.5;
+ cy1 = lenToDouble(attr, QLatin1String("cy")) + 0.5;
+ crx = lenToDouble(attr, QLatin1String("r"));
+ pt->drawEllipse(QRectF(cx1-crx, cy1-crx, 2*crx, 2*crx));
+ break;
+ case EllipseElement:
+ cx1 = lenToDouble(attr, QLatin1String("cx")) + 0.5;
+ cy1 = lenToDouble(attr, QLatin1String("cy")) + 0.5;
+ crx = lenToDouble(attr, QLatin1String("rx"));
+ cry = lenToDouble(attr, QLatin1String("ry"));
+ pt->drawEllipse(QRectF(cx1-crx, cy1-cry, 2*crx, 2*cry));
+ break;
+ case LineElement:
+ {
+ x1 = lenToDouble(attr, QLatin1String("x1"));
+ x2 = lenToDouble(attr, QLatin1String("x2"));
+ y1 = lenToDouble(attr, QLatin1String("y1"));
+ y2 = lenToDouble(attr, QLatin1String("y2"));
+ QPen p = pt->pen();
+ w = p.width();
+ p.setWidth((unsigned int)(w * (qAbs(pt->worldMatrix().m11()) + qAbs(pt->worldMatrix().m22())) / 2));
+ pt->setPen(p);
+ pt->drawLine(QLineF(x1, y1, x2, y2));
+ p.setWidthF(w);
+ pt->setPen(p);
+ }
+ break;
+ case PolylineElement:
+ case PolygonElement:
+ {
+ QString pts = attr.namedItem(QLatin1String("points")).nodeValue();
+ pts = pts.simplified();
+ QStringList sl = pts.split(QRegExp(QString::fromLatin1("[,\\s]")),
+ QString::SkipEmptyParts);
+ QPolygonF ptarr((uint) sl.count() / 2);
+ for (int i = 0; i < (int) sl.count() / 2; i++) {
+ double dx = sl[2*i].toDouble();
+ double dy = sl[2*i+1].toDouble();
+ ptarr[i] = QPointF(dx, dy);
+ }
+ if (t == PolylineElement) {
+ if (pt->brush().style() != Qt::NoBrush) {
+ QPen pn = pt->pen();
+ pt->setPen(Qt::NoPen);
+ pt->drawPolygon(ptarr);
+ pt->setPen(pn);
+ }
+ pt->drawPolyline(ptarr); // ### closes when filled. bug ?
+ } else {
+ pt->drawPolygon(ptarr);
+ }
+ }
+ break;
+ case SvgElement:
+ case GroupElement:
+ case AnchorElement:
+ {
+ QDomNode child = node.firstChild();
+ while (!child.isNull()) {
+ play(child, pt);
+ child = child.nextSibling();
+ }
+ }
+ break;
+ case PathElement:
+ drawPath(attr.namedItem(QLatin1String("d")).nodeValue(), pt);
+ break;
+ case TSpanElement:
+ case TextElement:
+ {
+ if (attr.contains(QLatin1String("x")))
+ curr->textx = lenToDouble(attr, QLatin1String("x"));
+ if (attr.contains(QLatin1String("y")))
+ curr->texty = lenToDouble(attr, QLatin1String("y"));
+ if (t == TSpanElement) {
+ curr->textx += lenToDouble(attr, QLatin1String("dx"));
+ curr->texty += lenToDouble(attr, QLatin1String("dy"));
+ }
+ // backup old colors
+ QPen pn = pt->pen();
+ QColor pcolor = pn.color();
+ QColor bcolor = pt->brush().color();
+ QDomNode c = node.firstChild();
+ while (!c.isNull()) {
+ if (c.isText()) {
+ // we have pen and brush reversed for text drawing
+ pn.setColor(bcolor);
+ pt->setPen(pn);
+ QString text = c.toText().nodeValue();
+ text = text.simplified(); // ### 'preserve'
+ w = pt->fontMetrics().width(text);
+ if (curr->textalign == Qt::AlignHCenter)
+ curr->textx -= w / 2;
+ else if (curr->textalign == Qt::AlignRight)
+ curr->textx -= w;
+ pt->drawText(QPointF(curr->textx, curr->texty), text);
+ // restore pen
+ pn.setColor(pcolor);
+ pt->setPen(pn);
+ curr->textx += w;
+ } else if (c.isElement() && c.toElement().tagName() == QLatin1String("tspan")) {
+ play(c, pt);
+
+ }
+ c = c.nextSibling();
+ }
+ if (t == TSpanElement) {
+ // move current text position in parent text element
+ StateList::Iterator it = --(--stack.end());
+ (*it).textx = curr->textx;
+ (*it).texty = curr->texty;
+ }
+ }
+ break;
+ case ImageElement:
+ {
+ x1 = lenToDouble(attr, QLatin1String("x"));
+ y1 = lenToDouble(attr, QLatin1String("y"));
+ w = lenToDouble(attr, QLatin1String("width"));
+ h = lenToDouble(attr, QLatin1String("height"));
+ QString href = attr.namedItem(QLatin1String("xlink:href")).nodeValue();
+ // ### catch references to embedded .svg files
+ QPixmap pix;
+ if (!pix.load(href)){
+ qWarning("Q3SVGPaintEngine::play: Couldn't load image %s",href.latin1());
+ break;
+ }
+ pt->drawPixmap(QRectF(x1, y1, w, h), pix, QRectF());
+ }
+ break;
+ case DescElement:
+ case TitleElement:
+ // ignored for now
+ break;
+ case ClipElement:
+ {
+ restoreAttributes(pt); // To ensure the clip rect is saved, we need to restore now
+ QDomNode child = node.firstChild();
+ QDomNamedNodeMap childAttr = child.attributes();
+ if (child.nodeName() == QLatin1String("rect")) {
+ QRect r;
+ r.setX(lenToInt(childAttr, QLatin1String("x")));
+ r.setY(lenToInt(childAttr, QLatin1String("y")));
+ r.setWidth(lenToInt(childAttr, QLatin1String("width")));
+ r.setHeight(lenToInt(childAttr, QLatin1String("height")));
+ pt->setClipRect(r);
+ } else if (child.nodeName() == QLatin1String("ellipse")) {
+ QRect r;
+ int x = lenToInt(childAttr, QLatin1String("cx"));
+ int y = lenToInt(childAttr, QLatin1String("cy"));
+ int width = lenToInt(childAttr, QLatin1String("rx"));
+ int height = lenToInt(childAttr, QLatin1String("ry"));
+ r.setX(x - width);
+ r.setY(y - height);
+ r.setWidth(width * 2);
+ r.setHeight(height * 2);
+ QRegion rgn(r, QRegion::Ellipse);
+ pt->setClipRegion(rgn);
+ }
+ break;
+ }
+ case InvalidElement:
+ qWarning("Q3SVGPaintEngine::play: unknown element type %s", node.nodeName().latin1());
+ break;
+ }
+
+ if (t != ClipElement)
+ restoreAttributes(pt);
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Parse a <length> datatype consisting of a number followed by an
+ optional unit specifier. Can be used for type <coordinate> as
+ well. For relative units the value of \a horiz will determine
+ whether the horizontal or vertical dimension will be used.
+*/
+double Q3SVGPaintEnginePrivate::parseLen(const QString &str, bool *ok, bool horiz) const
+{
+ QRegExp reg(QString::fromLatin1("([+-]?\\d*\\.*\\d*[Ee]?[+-]?\\d*)(em|ex|px|%|pt|pc|cm|mm|in|)$"));
+ if (reg.indexIn(str) == -1) {
+ qWarning("Q3SVGPaintEngine::parseLen: couldn't parse %s", str.latin1());
+ if (ok)
+ *ok = false;
+ return 0.0;
+ }
+
+ double dbl = reg.cap(1).toDouble();
+ QString u = reg.cap(2);
+ if (!u.isEmpty() && u != QLatin1String("px")) {
+ if (u == QLatin1String("em")) {
+ QFontInfo fi(cfont);
+ dbl *= fi.pixelSize();
+ } else if (u == QLatin1String("ex")) {
+ QFontInfo fi(cfont);
+ dbl *= 0.5 * fi.pixelSize();
+ } else if (u == QLatin1String("%"))
+ dbl *= (horiz ? wwidth : wheight)/100.0;
+ else if (u == QLatin1String("cm"))
+ dbl *= dev->logicalDpiX() / 2.54;
+ else if (u == QLatin1String("mm"))
+ dbl *= dev->logicalDpiX() / 25.4;
+ else if (u == QLatin1String("in"))
+ dbl *= dev->logicalDpiX();
+ else if (u == QLatin1String("pt"))
+ dbl *= dev->logicalDpiX() / 72.0;
+ else if (u == QLatin1String("pc"))
+ dbl *= dev->logicalDpiX() / 6.0;
+ else
+ qWarning("Q3SVGPaintEngine::parseLen: Unknown unit %s", u.latin1());
+ }
+ if (ok)
+ *ok = true;
+ return dbl;
+}
+
+/*!
+ \internal
+
+ Returns the length specified in attribute \a attr in \a map. If
+ the specified attribute doesn't exist or can't be parsed \a def is
+ returned.
+*/
+
+int Q3SVGPaintEnginePrivate::lenToInt(const QDomNamedNodeMap &map, const QString &attr, int def) const
+{
+ if (map.contains(attr)) {
+ bool ok;
+ double dbl = parseLen(map.namedItem(attr).nodeValue(), &ok);
+ if (ok)
+ return qRound(dbl);
+ }
+ return def;
+}
+
+double Q3SVGPaintEnginePrivate::lenToDouble(const QDomNamedNodeMap &map, const QString &attr,
+ int def) const
+{
+ if (map.contains(attr)) {
+ bool ok;
+ double x = parseLen(map.namedItem(attr).nodeValue(), &ok);
+ if (ok) return x;
+ }
+ return static_cast<double>(def);
+}
+
+void Q3SVGPaintEnginePrivate::setTransform(const QString &tr, QPainter *pt)
+{
+ QString t = tr.simplified();
+
+ QRegExp reg(QString::fromLatin1("\\s*([\\w]+)\\s*\\(([^\\(]*)\\)"));
+ int index = 0;
+ while ((index = reg.indexIn(t, index)) >= 0) {
+ QString command = reg.cap(1);
+ QString params = reg.cap(2);
+ QStringList plist = params.split(QRegExp(QString::fromLatin1("[,\\s]")),
+ QString::SkipEmptyParts);
+ if (command == QLatin1String("translate")) {
+ double tx = 0, ty = 0;
+ tx = plist[0].toDouble();
+ if (plist.count() >= 2)
+ ty = plist[1].toDouble();
+ pt->translate(tx, ty);
+ } else if (command == QLatin1String("rotate")) {
+ pt->rotate(plist[0].toDouble());
+ } else if (command == QLatin1String("scale")) {
+ double sx, sy;
+ sx = sy = plist[0].toDouble();
+ if (plist.count() >= 2)
+ sy = plist[1].toDouble();
+ pt->scale(sx, sy);
+ } else if (command == QLatin1String("matrix") && plist.count() >= 6) {
+ double m[6];
+ for (int i = 0; i < 6; i++)
+ m[i] = plist[i].toDouble();
+ QMatrix wm(m[0], m[1], m[2], m[3], m[4], m[5]);
+ pt->setWorldMatrix(wm, true);
+ } else if (command == QLatin1String("skewX")) {
+ pt->shear(0.0, tan(plist[0].toDouble() * deg2rad));
+ } else if (command == QLatin1String("skewY")) {
+ pt->shear(tan(plist[0].toDouble() * deg2rad), 0.0);
+ }
+
+ // move on to next command
+ index += reg.matchedLength();
+ }
+}
+/*!
+ \internal
+
+ Push the current drawing attributes on a stack.
+
+ \sa restoreAttributes()
+*/
+
+void Q3SVGPaintEnginePrivate::saveAttributes(QPainter *pt)
+{
+ pt->save();
+ // copy old state
+ Q3SVGPaintEngineState st(*curr);
+ stack.append(st);
+ curr = &stack.last();
+}
+
+/*!
+ \internal
+
+ Pop the current drawing attributes off the stack.
+
+ \sa saveAttributes()
+*/
+
+void Q3SVGPaintEnginePrivate::restoreAttributes(QPainter *pt)
+{
+ pt->restore();
+ Q_ASSERT(stack.count() > 1);
+ stack.removeLast();
+ curr = &stack.last();
+}
+
+void Q3SVGPaintEnginePrivate::setStyle(const QString &s, QPainter *pt)
+{
+ QStringList rules = s.split(QLatin1Char(';'), QString::SkipEmptyParts);
+
+ QPen pen = pt->pen();
+ QFont font = pt->font();
+
+ QStringList::ConstIterator it = rules.constBegin();
+ for (; it != rules.constEnd(); it++) {
+ int col = (*it).indexOf(QLatin1Char(':'));
+ if (col > 0) {
+ QString prop = (*it).left(col).simplified();
+ QString val = (*it).right((*it).length() - col - 1);
+ val = val.toLower().trimmed();
+ setStyleProperty(prop, val, &pen, &font, &curr->textalign, pt);
+ }
+ }
+ pt->setPen(pen);
+ pt->setFont(font);
+}
+
+void Q3SVGPaintEnginePrivate::setStyleProperty(const QString &prop, const QString &val, QPen *pen,
+ QFont *font, int *talign, QPainter *pt)
+{
+ if (prop == QLatin1String("stroke")) {
+ if (val == QLatin1String("none")) {
+ pen->setStyle(Qt::NoPen);
+ } else {
+ pen->setColor(parseColor(val));
+ if (pen->style() == Qt::NoPen)
+ pen->setStyle(Qt::SolidLine);
+ if (pen->width() == 0)
+ pen->setWidth(1);
+ }
+ } else if (prop == QLatin1String("stroke-opacity")) {
+ double opacity = parseLen(val);
+ QColor c = pen->color();
+ c.setAlpha((int)(opacity*255));
+ pen->setColor(c);
+ } else if (prop == QLatin1String("fill-opacity")) {
+ double opacity = parseLen(val);
+ QColor c = pt->brush().color();
+ c.setAlpha((int)(opacity*255));
+ pt->setBrush(c);
+ } else if (prop == QLatin1String("stroke-width")) {
+ double w = parseLen(val);
+ if (w > 0.0001)
+ pen->setWidth(int(w));
+ else
+ pen->setStyle(Qt::NoPen);
+ } else if (prop == QLatin1String("stroke-linecap")) {
+ if (val == QLatin1String("butt"))
+ pen->setCapStyle(Qt::FlatCap);
+ else if (val == QLatin1String("round"))
+ pen->setCapStyle(Qt::RoundCap);
+ else if (val == QLatin1String("square"))
+ pen->setCapStyle(Qt::SquareCap);
+ } else if (prop == QLatin1String("stroke-linejoin")) {
+ if (val == QLatin1String("miter"))
+ pen->setJoinStyle(Qt::MiterJoin);
+ else if (val == QLatin1String("round"))
+ pen->setJoinStyle(Qt::RoundJoin);
+ else if (val == QLatin1String("bevel"))
+ pen->setJoinStyle(Qt::BevelJoin);
+ } else if (prop == QLatin1String("stroke-dasharray")) {
+ if (val == QLatin1String("18,6"))
+ pen->setStyle(Qt::DashLine);
+ else if (val == QLatin1String("3"))
+ pen->setStyle(Qt::DotLine);
+ else if (val == QLatin1String("9,6,3,6"))
+ pen->setStyle(Qt::DashDotLine);
+ else if (val == QLatin1String("9,3,3"))
+ pen->setStyle(Qt::DashDotDotLine);
+ } else if (prop == QLatin1String("fill")) {
+ if (val == QLatin1String("none"))
+ pt->setBrush(Qt::NoBrush);
+ else
+ pt->setBrush(parseColor(val));
+ } else if (prop == QLatin1String("font-size")) {
+ font->setPixelSize(qRound(parseLen(val)));
+ } else if (prop == QLatin1String("font-family")) {
+ font->setFamily(val);
+ } else if (prop == QLatin1String("font-style")) {
+ if (val == QLatin1String("normal"))
+ font->setItalic(false);
+ else if (val == QLatin1String("italic"))
+ font->setItalic(true);
+ else
+ qWarning("QSvgDevice::setStyleProperty: unhandled font-style: %s", val.latin1());
+ } else if (prop == QLatin1String("font-weight")) {
+ int w = font->weight();
+ // no exact equivalents so we have to QLatin1String("round") a little bit
+ if (val == QLatin1String("100") || val == QLatin1String("200"))
+ w = QFont::Light;
+ if (val == QLatin1String("300") || val == QLatin1String("400") || val == QLatin1String("normal"))
+ w = QFont::Normal;
+ else if (val == QLatin1String("500") || val == QLatin1String("600"))
+ w = QFont::DemiBold;
+ else if (val == QLatin1String("700") || val == QLatin1String("bold") || val == QLatin1String("800"))
+ w = QFont::Bold;
+ else if (val == QLatin1String("900"))
+ w = QFont::Black;
+ font->setWeight(w);
+ } else if (prop == QLatin1String("text-anchor")) {
+ if (val == QLatin1String("middle"))
+ *talign = Qt::AlignHCenter;
+ else if (val == QLatin1String("end"))
+ *talign = Qt::AlignRight;
+ else
+ *talign = Qt::AlignLeft;
+ }
+}
+
+void Q3SVGPaintEnginePrivate::drawPath(const QString &data, QPainter *pt)
+{
+ double x0 = 0, y0 = 0; // starting point
+ double x = 0, y = 0; // current point
+ QPointF ctrlPt;
+ QPainterPath path; // resulting path
+ int idx = 0; // current data position
+ int mode = 0, lastMode = 0; // parser state
+ bool relative = false; // e.g. 'h' vs. 'H'
+ QString commands(QLatin1String("MZLHVCSQTA")); // recognized commands
+ int cmdArgs[] = { 2, 0, 2, 1, 1, 6, 4, 4, 2, 7 }; // no of arguments
+ QRegExp reg(QString::fromLatin1("\\s*,?\\s*([+-]?\\d*\\.?\\d*)")); // floating point
+
+ // detect next command
+ while (idx < data.length()) {
+ QChar ch = data[(int)idx++];
+ if (ch.isSpace())
+ continue;
+ QChar chUp = ch.toUpper();
+ int cmd = commands.indexOf(chUp);
+ if (cmd >= 0) {
+ // switch to new command mode
+ mode = cmd;
+ relative = (ch != chUp); // e.g. 'm' instead of 'M'
+ } else {
+ if (mode && !ch.isLetter()) {
+ cmd = mode; // continue in previous mode
+ idx--;
+ } else {
+ qWarning("Q3SVGPaintEngine::drawPath: Unknown command");
+ return;
+ }
+ }
+
+ // read in the required number of arguments
+ const int maxArgs = 7;
+ double arg[maxArgs];
+ int numArgs = cmdArgs[cmd];
+ for (int i = 0; i < numArgs; i++) {
+ int pos = reg.indexIn(data, idx);
+ if (pos == -1) {
+ qWarning("Q3SVGPaintEngine::drawPath: Error parsing arguments");
+ return;
+ }
+ arg[i] = reg.cap(1).toDouble();
+ idx = pos + reg.matchedLength();
+ };
+
+ // process command
+ double offsetX = relative ? x : 0; // correction offsets
+ double offsetY = relative ? y : 0; // for relative commands
+ switch (mode) {
+ case 0: // 'M' move to
+ x = x0 = arg[0] + offsetX;
+ y = y0 = arg[1] + offsetY;
+ path.moveTo(x0, y0);
+ mode = 2; // -> 'L'
+ break;
+ case 1: // 'Z' close path
+ x = x0;
+ y = y0;
+ path.closeSubpath();
+ mode = 0;
+ break;
+ case 2: // 'L' line to
+ x = arg[0] + offsetX;
+ y = arg[1] + offsetY;
+ path.lineTo(x, y);
+ break;
+ case 3: // 'H' horizontal line
+ x = arg[0] + offsetX;
+ path.lineTo(x, y);
+ break;
+ case 4: // 'V' vertical line
+ y = arg[0] + offsetY;
+ path.lineTo(x, y);
+ break;
+ case 5: { // 'C' cubic bezier curveto
+ QPointF c1(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF c2(arg[2]+offsetX, arg[3]+offsetY);
+ QPointF e(arg[4]+offsetX, arg[5]+offsetY);
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 6: { // 'S' smooth shorthand
+ QPointF c1;
+ if (lastMode == 5 || lastMode == 6)
+ c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+ else
+ c1 = QPointF(x, y);
+ QPointF c2(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF e(arg[2]+offsetX, arg[3]+offsetY);
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 7: { // 'Q' quadratic bezier curves
+ QPointF c(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF e(arg[2]+offsetX, arg[3]+offsetY);
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 8: { // 'T' smooth shorthand
+ QPointF e(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF c;
+ if (lastMode == 7 || lastMode == 8)
+ c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+ else
+ c = QPointF(x, y);
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 9: // 'A' elliptical arc curve
+ // ### just a straight line
+ x = arg[5] + offsetX;
+ y = arg[6] + offsetY;
+ path.lineTo(x, y);
+ break;
+ };
+ lastMode = mode;
+ }
+ pt->drawPath(path);
+}
+
+/*!
+ \internal
+
+ Parses a CSS2-compatible color specification. Either a keyword or
+ a numerical RGB specification like #ff00ff or rgb(255,0,50%).
+*/
+
+QColor Q3SVGPaintEnginePrivate::parseColor(const QString &col)
+{
+ static const struct ColorTable {
+ const char *name;
+ const char *rgb;
+ } coltab[] = {
+ { "black", "#000000" },
+ { "silver", "#c0c0c0" },
+ { "gray", "#808080" },
+ { "white", "#ffffff" },
+ { "maroon", "#800000" },
+ { "red", "#ff0000" },
+ { "purple", "#800080" },
+ { "fuchsia", "#ff00ff" },
+ { "green", "#008000" },
+ { "lime", "#00ff00" },
+ { "olive", "#808000" },
+ { "yellow", "#ffff00" },
+ { "navy", "#000080" },
+ { "blue", "#0000ff" },
+ { "teal", "#008080" },
+ { "aqua", "#00ffff" },
+ // ### the latest spec has more
+ { 0, 0 }
+ };
+
+ // initialize color map on first use
+ if (!qSvgColMap) {
+ qSvgColMap = new QMap<QString,QString>;
+ const struct ColorTable *t = coltab;
+ while (t->name) {
+ qSvgColMap->insert(QLatin1String(t->name), QLatin1String(t->rgb));
+ ++t;
+ }
+ }
+
+ // a keyword?
+ if (qSvgColMap->contains(col))
+ return QColor((*qSvgColMap)[col]);
+ // in rgb(r,g,b) form ?
+ QString c = col;
+ c.replace(QRegExp(QString::fromLatin1("\\s*")), QLatin1String(""));
+ QRegExp reg(QString::fromLatin1("^rgb\\((\\d+)(%?),(\\d+)(%?),(\\d+)(%?)\\)$"));
+ if (reg.indexIn(c) >= 0) {
+ int comp[3];
+ for (int i = 0; i < 3; i++) {
+ comp[i] = reg.cap(2*i+1).toInt();
+ if (!reg.cap(2*i+2).isEmpty()) // percentage ?
+ comp[i] = int((double(255*comp[i])/100.0));
+ }
+ return QColor(comp[0], comp[1], comp[2]);
+ }
+
+ // check for predefined Qt color objects, #RRGGBB and #RGB
+ return QColor(col);
+}
+
+static QString qt_svg_compose_path(const QPainterPath &path)
+{
+ QString str, tmp;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &elm = path.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::LineToElement:
+ tmp.sprintf("L %f %f ", elm.x, elm.y);
+ str += tmp;
+ break;
+ case QPainterPath::MoveToElement:
+ tmp.sprintf("M %f %f ", elm.x, elm.y);
+ str += tmp;
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ Q_ASSERT(path.elementCount() > i+2);
+ const QPainterPath::Element cd1 = path.elementAt(i+1);
+ const QPainterPath::Element cd2 = path.elementAt(i+2);
+ Q_ASSERT(cd1.type == QPainterPath::CurveToDataElement
+ && cd2.type == QPainterPath::CurveToDataElement);
+ tmp.sprintf("C %f %f %f %f %f %f ", elm.x, elm.y, cd1.x, cd1.y, cd2.x, cd2.y);
+ str += tmp;
+ i += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return str;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/painting/q3paintengine_svg_p.h b/src/qt3support/painting/q3paintengine_svg_p.h
new file mode 100644
index 0000000..6df805a
--- /dev/null
+++ b/src/qt3support/painting/q3paintengine_svg_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PAINTENGINE_SVG_P_H
+#define Q3PAINTENGINE_SVG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtXml/qdom.h"
+#include "QtGui/qpaintengine.h"
+#include "private/qpicture_p.h" // for QPaintCommands
+
+QT_BEGIN_NAMESPACE
+
+class Q3SVGPaintEnginePrivate;
+
+class Q3SVGPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(Q3SVGPaintEngine)
+
+public:
+ Q3SVGPaintEngine();
+ ~Q3SVGPaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &origin);
+ void updateFont(const QFont &font);
+ void updateBackground(Qt::BGMode bgmode, const QBrush &bgBrush);
+ void updateMatrix(const QMatrix &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateClipPath(const QPainterPath &path, Qt::ClipOperation op);
+ void updateRenderHints(QPainter::RenderHints hints);
+
+ void drawEllipse(const QRect &r);
+ void drawLine(const QLineF &line);
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawRect(const QRectF &r);
+ void drawPoint(const QPointF &p);
+ void drawPoints(const QPointF *points, int pointCount);
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+#if defined Q_WS_WIN // ### not liking this!!
+ HDC handle() const { return 0; }
+#else
+ Qt::HANDLE handle() const {return 0; }
+#endif
+ Type type() const { return SVG; }
+ bool play(QPainter *p);
+
+ QString toString() const;
+
+ bool load(QIODevice *dev);
+ bool save(QIODevice *dev);
+ bool save(const QString &fileName);
+
+ QRect boundingRect() const;
+ void setBoundingRect(const QRect &r);
+
+protected:
+ Q3SVGPaintEngine(Q3SVGPaintEnginePrivate &dptr);
+
+private:
+ Q_DISABLE_COPY(Q3SVGPaintEngine)
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3PAINTENGINE_SVG_P_H
diff --git a/src/qt3support/painting/q3painter.cpp b/src/qt3support/painting/q3painter.cpp
new file mode 100644
index 0000000..216b181
--- /dev/null
+++ b/src/qt3support/painting/q3painter.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3painter.h"
+#include "qpaintengine.h"
+
+#include <private/qpainter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3Painter
+ \brief The Q3Painter class is a Qt 3 compatibility wrapper for QPainter.
+
+ \compat
+
+ Prior to Qt 4, QPainter specialized the pen drawing for rectangle
+ based functions (in particular: drawRect, drawEllipse,
+ drawRoundRect, drawArc, drawChord and drawPie). When stroking a
+ rectangle of width 10, the pen would draw a rectangle of width 10.
+ Drawing a polygon defined by the corner points of the same
+ rectangle the stroke would have a width of 11.
+
+ The reason for this is best explained using the picture below:
+
+ \img q3painter_rationale.png
+
+ As we can see, stroking the rectangle so it gets a width of 10,
+ means the pen is drawn on a rectangle on width 9. The polygon,
+ however follows a consistent model.
+
+ In Qt 4, all rectangle based functions have changed to follow the
+ polygon approach, which means that the rectangle defines the size of
+ the fill, and the pen follows the edges of the shape. For pen widths
+ of 0 and 1 this means that the stroke will be inside the shape on the
+ left and the top and outside on the bottom and right.
+
+ The reason for the change in Qt 4 is so that we provide consistency
+ for all drawing functions even with complex transformations.
+*/
+
+/*!
+ \fn Q3Painter::Q3Painter()
+
+ Constructs a Q3Painter.
+*/
+
+/*!
+ \fn Q3Painter::Q3Painter(QPaintDevice *pdev)
+
+ Constructs a Q3Painter that operates on device \a pdev.
+*/
+
+/*!
+ \internal
+*/
+QRect Q3Painter::adjustedRectangle(const QRect &r)
+{
+ QRect rect = r.normalized();
+ int subtract = d_func()->rectSubtraction();
+ if (subtract != 0)
+ rect.setSize(QSize(rect.width() - subtract, rect.height() - subtract));
+ return rect;
+}
+
+
+/*!
+ \fn void Q3Painter::drawRect(int x, int y, int w, int h)
+
+ \overload
+
+ Draws the rectangle that fits inside the bounds specified by \a x,
+ \a y, \a w and \a h using the current pen and brush.
+*/
+
+/*!
+ \fn void Q3Painter::drawRect(const QRect &r)
+
+ Draws a rectangle that fits inside the rectangle \a r using the
+ current pen and brush.
+
+*/
+
+
+
+/*!
+ \fn Q3Painter::drawEllipse(const QRect &r)
+
+ Draws the ellipse that fits inside the bounds \a r using the
+ current pen and brush.
+
+*/
+
+/*!
+ \fn Q3Painter::drawEllipse(int x, int y, int width, int height)
+
+ \overload
+
+ Draws an ellipse that fits inside the bounds specified by \a x,
+ \a y, \a width and \a height using the current pen and brush.
+
+*/
+
+/*!
+ \fn void Q3Painter::drawPie(int x, int y, int w, int h, int
+ startAngle, int spanAngle)
+
+ \overload
+
+ Draws a pie segment that fits inside the bounds (\a{x}, \a{y},
+ \a{w}, \a{h}) with the given \a startAngle and \a spanAngle.
+*/
+
+/*!
+ \fn void Q3Painter::drawPie(const QRect &r, int a, int alen)
+
+ Draws a pie defined by the rectangle \a r, the start angle \a a
+ and the arc length \a alen.
+
+ The pie is filled with the current brush().
+
+ The angles \a a and \a alen are 1/16th of a degree, i.e. a full
+ circle equals 5760 (16*360). Positive values of \a a and \a alen
+ mean counter-clockwise while negative values mean the clockwise
+ direction. Zero degrees is at the 3 o'clock position.
+
+ \sa drawArc(), drawChord()
+*/
+
+/*!
+ \fn void Q3Painter::drawArc(int x, int y, int w, int h, int
+ startAngle, int spanAngle)
+
+ \overload
+
+ Draws the arc that fits inside the rectangle (\a{x}, \a{y}, \a{w},
+ \a{h}), with the given \a startAngle and \a spanAngle.
+*/
+
+/*!
+ \fn void Q3Painter::drawArc(const QRect &r, int a, int alen)
+
+ Draws an arc defined by the rectangle \a r, the start angle \a a
+ and the arc length \a alen.
+
+ The angles \a a and \a alen are 1/16th of a degree, i.e. a full
+ circle equals 5760 (16*360). Positive values of \a a and \a alen
+ mean counter-clockwise while negative values mean the clockwise
+ direction. Zero degrees is at the 3 o'clock position.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_painting_q3painter.cpp 0
+
+ \sa drawPie(), drawChord()
+*/
+
+/*!
+ \fn void Q3Painter::drawChord(int x, int y, int w, int h, int
+ startAngle, int spanAngle)
+
+ \overload
+
+ Draws a chord that fits inside the rectangle (\a{x}, \a{y}, \a{w},
+ \a{h}) with the given \a startAngle and \a spanAngle.
+*/
+
+
+/*!
+ \fn void Q3Painter::drawChord(const QRect &r, int a, int alen)
+
+ Draws a chord defined by the rectangle \a r, the start angle \a a
+ and the arc length \a alen.
+
+ The chord is filled with the current brush().
+
+ The angles \a a and \a alen are 1/16th of a degree, i.e. a full
+ circle equals 5760 (16*360). Positive values of \a a and \a alen
+ mean counter-clockwise while negative values mean the clockwise
+ direction. Zero degrees is at the 3 o'clock position.
+
+ \sa drawArc(), drawPie()
+*/
+
+/*!
+ \fn void Q3Painter::drawRoundRect(const QRect &r, int xrnd, int yrnd)
+
+ Draws a rounded rect that fits into the bounds \a r using the current
+ pen and brush. The parameters \a xrnd and \a yrnd specifies the roundness
+ in x and y direction.
+*/
+
+/*!
+ \fn void Q3Painter::drawRoundRect(int x, int y, int w, int h, int xrnd, int yrnd)
+
+ \overload
+
+ Draws a rounded rect that fits into the bounds \a x, \a y, \a w
+ and \a h using the current pen and brush. The parameters \a xrnd
+ and \a yrnd specifies the roundness in x and y direction.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/painting/q3painter.h b/src/qt3support/painting/q3painter.h
new file mode 100644
index 0000000..a48158c
--- /dev/null
+++ b/src/qt3support/painting/q3painter.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PAINTER_H
+#define Q3PAINTER_H
+
+#include <QtGui/qpainter.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3Painter : public QPainter
+{
+public:
+ Q3Painter() : QPainter() { }
+ Q3Painter(QPaintDevice *pdev) : QPainter(pdev) { }
+
+ inline void drawRect(const QRect &rect);
+ inline void drawRect(int x1, int y1, int w, int h)
+ { drawRect(QRect(x1, y1, w, h)); }
+
+ inline void drawRoundRect(const QRect &r, int xround = 25, int yround = 25);
+ inline void drawRoundRect(int x, int y, int w, int h, int xround = 25, int yround = 25)
+ { drawRoundRect(QRect(x, y, w, h), xround, yround); }
+
+ inline void drawEllipse(const QRect &r);
+ inline void drawEllipse(int x, int y, int w, int h)
+ { drawEllipse(QRect(x, y, w, h)); }
+
+ inline void drawArc(const QRect &r, int a, int alen);
+ inline void drawArc(int x, int y, int w, int h, int a, int alen)
+ { drawArc(QRect(x, y, w, h), a, alen); }
+
+ inline void drawPie(const QRect &r, int a, int alen);
+ inline void drawPie(int x, int y, int w, int h, int a, int alen)
+ { drawPie(QRect(x, y, w, h), a, alen); }
+
+ inline void drawChord(const QRect &r, int a, int alen);
+ inline void drawChord(int x, int y, int w, int h, int a, int alen)
+ { drawChord(QRect(x, y, w, h), a, alen); }
+
+private:
+ QRect adjustedRectangle(const QRect &r);
+
+ Q_DISABLE_COPY(Q3Painter)
+};
+
+void inline Q3Painter::drawRect(const QRect &r)
+{
+ QPainter::drawRect(adjustedRectangle(r));
+}
+
+void inline Q3Painter::drawEllipse(const QRect &r)
+{
+ QPainter::drawEllipse(adjustedRectangle(r));
+}
+
+void inline Q3Painter::drawRoundRect(const QRect &r, int xrnd, int yrnd)
+{
+ QPainter::drawRoundRect(adjustedRectangle(r), xrnd, yrnd);
+}
+
+void inline Q3Painter::drawArc(const QRect &r, int angle, int arcLength)
+{
+ QPainter::drawArc(adjustedRectangle(r), angle, arcLength);
+}
+
+void inline Q3Painter::drawPie(const QRect &r, int angle, int arcLength)
+{
+ QPainter::drawPie(adjustedRectangle(r), angle, arcLength);
+}
+
+void inline Q3Painter::drawChord(const QRect &r, int angle, int arcLength)
+{
+ QPainter::drawChord(adjustedRectangle(r), angle, arcLength);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PAINTER_H
diff --git a/src/qt3support/painting/q3picture.cpp b/src/qt3support/painting/q3picture.cpp
new file mode 100644
index 0000000..dd851ba
--- /dev/null
+++ b/src/qt3support/painting/q3picture.cpp
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpicture_p.h"
+#include "qfile.h"
+#include "qpainter.h"
+#include "q3picture.h"
+#include "q3paintengine_svg_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SvgDevice : public QPaintDevice
+{
+public:
+ Q3SvgDevice() : QPaintDevice() {}
+ bool load(QIODevice *dev) { return svgEngine.load(dev); }
+ bool save(const QString &fileName) { return svgEngine.save(fileName); }
+ bool save(QIODevice *dev) { return svgEngine.save(dev); }
+ void setBoundingRect(const QRect &rect) { svgEngine.setBoundingRect(rect); }
+ QRect boundingRect() const { return svgEngine.boundingRect(); }
+ QPaintEngine *paintEngine() const { return (QPaintEngine *)&svgEngine; }
+ bool play(QPainter *p) { return svgEngine.play(p); }
+ int metric(PaintDeviceMetric m) const;
+
+private:
+ Q3SVGPaintEngine svgEngine;
+};
+
+int Q3SvgDevice::metric(PaintDeviceMetric m) const
+{
+ int val;
+ QRect br = svgEngine.boundingRect();
+ switch (m) {
+ case PdmWidth:
+ val = br.width();
+ break;
+ case PdmHeight:
+ val = br.height();
+ break;
+ case PdmWidthMM:
+ val = int(25.4/72.0*br.width());
+ break;
+ case PdmHeightMM:
+ val = int(25.4/72.0*br.height());
+ break;
+ case PdmDpiX:
+ val = 72;
+ break;
+ case PdmDpiY:
+ val = 72;
+ break;
+ case PdmNumColors:
+ val = 16777216;
+ break;
+ case PdmDepth:
+ val = 24;
+ break;
+ default:
+ val = 0;
+ qWarning("Q3SvgDevice::metric: Invalid metric command");
+ }
+ return val;
+}
+
+/*!
+ \class Q3Picture
+ \brief The Q3Picture class is a paint device that records and
+ replays Q3Painter commands.
+
+ \compat
+
+ Q3Picture can also read and write SVG (Scalable Vector Graphics)
+ files, a \l{http://www.w3.org/Graphics/SVG/} {W3C} XML format.
+ Note that when using the load() and save() functions to read and
+ write SVG files, the format must be specified.
+
+ \sa QPicture
+*/
+
+/*!
+ \fn Q3Picture::Q3Picture()
+
+ Constructs a Q3Picture.
+*/
+
+/*!
+ \fn Q3Picture::Q3Picture(const QPicture &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ \overload
+ Loads the picture in the specified \a format from a file with the
+ given \a fileName. Returns true if the file is loaded successfully;
+ otherwise returns false.
+*/
+bool Q3Picture::load(const QString &fileName, const char *format)
+{
+ QFile f(fileName);
+ if (!f.open(QIODevice::ReadOnly))
+ return false;
+ return load(&f, format);
+}
+
+/*!
+ \fn bool Q3Picture::load(QIODevice *device, const char *format)
+
+ Loads the picture in the specified \a format from the given \a device.
+ Returns true if the file is loaded successfully; otherwise returns false.
+
+ Note that when using the load() function to read SVG files, the
+ format must be specified. For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_painting_q3picture.cpp 0
+
+ \sa save()
+*/
+bool Q3Picture::load(QIODevice *dev, const char *format)
+{
+ if (qstrcmp(format, "svg" ) == 0) {
+ Q3SvgDevice svg;
+ if (!svg.load(dev))
+ return false;
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing);
+ bool b = svg.play(&p);
+ d_func()->brect = svg.boundingRect();
+ return b;
+ }
+ return QPicture::load(dev, format);
+}
+
+/*!
+ \overload
+ Saves the picture in the specified \a format to the file with the
+ given \a fileName.
+
+ \sa load()
+*/
+bool Q3Picture::save(const QString &fileName, const char *format)
+{
+ if (paintingActive()) {
+ qWarning("Q3Picture::save: still being painted on. "
+ "Call QPainter::end() first");
+ return false;
+ }
+
+ // identical to QIODevice* code below but the file name
+ // makes a difference when it comes to saving pixmaps
+ if (qstricmp( format, "svg") == 0) {
+ Q3SvgDevice svg;
+ QPainter p(&svg);
+ if (!play(&p))
+ return false;
+ svg.setBoundingRect(boundingRect());
+ return svg.save(fileName);
+ }
+
+ return QPicture::save(fileName, format);
+}
+
+/*!
+ \fn bool Q3Picture::save(QIODevice *device, const char *format)
+
+ Saves the picture in the specified \a format to the given \a device.
+ Returns true if the save is successful. Returns false if, for
+ example, the picture is still being painted, i.e., QPainter::end()
+ has not yet been called.
+
+ Note that when using the save() function to save SVG files, the
+ format must be specified. For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_painting_q3picture.cpp 1
+
+ \sa load()
+*/
+bool Q3Picture::save(QIODevice *dev, const char *format)
+{
+ if (paintingActive()) {
+ qWarning("Q3Picture::save: still being painted on. "
+ "Call QPainter::end() first");
+ return false;
+ }
+
+ if (qstricmp(format, "svg") == 0) {
+ Q3SvgDevice svg;
+ QPainter p(&svg);
+ if (!play(&p))
+ return false;
+ svg.setBoundingRect(boundingRect());
+ return svg.save(dev);
+ }
+ return QPicture::save(dev, format);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/painting/q3picture.h b/src/qt3support/painting/q3picture.h
new file mode 100644
index 0000000..3236182
--- /dev/null
+++ b/src/qt3support/painting/q3picture.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PICTURE_H
+#define Q3PICTURE_H
+
+#include <QtGui/qpicture.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3Picture : public QPicture
+{
+public:
+ Q3Picture() : QPicture(-1) { }
+ Q3Picture(const QPicture &pic) : QPicture(pic) { }
+ bool load(QIODevice *dev, const char *format = 0);
+ bool load(const QString &fileName, const char *format = 0);
+ bool save(QIODevice *dev, const char *format = 0);
+ bool save(const QString &fileName, const char *format = 0);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PICTURE_H
diff --git a/src/qt3support/painting/q3pointarray.cpp b/src/qt3support/painting/q3pointarray.cpp
new file mode 100644
index 0000000..ee0bed3
--- /dev/null
+++ b/src/qt3support/painting/q3pointarray.cpp
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3pointarray.h"
+#include "private/qbezier_p.h"
+#include "private/qpainterpath_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3PointArray
+ The Q3PointArray class provides an array of points.
+
+ \compat
+
+ Q3PointArray is a QPolygon subclass that provides functions
+ to make it more source compatible with the \c QPointArray class
+ in Qt 3.
+
+ In Qt 4, we recommend that you use QPainterPath for representing
+ arcs, ellipses, and Bezier curves, rather than QPolygon.
+*/
+
+/*!
+ Sets the points of the array to those describing an arc of an
+ ellipse with size, width \a w by height \a h, and position (\a x,
+ \a y), starting from angle \a a1 and spanning by angle \a a2. The
+ resulting array has sufficient resolution for pixel accuracy (see
+ the overloaded function which takes an additional QMatrix
+ parameter).
+
+ Angles are specified in 16ths of a degree, i.e. a full circle
+ equals 5760 (16*360). Positive values mean counter-clockwise,
+ whereas negative values mean the clockwise direction. Zero degrees
+ is at the 3 o'clock position.
+*/
+#ifndef QT_NO_WMATRIX
+void Q3PointArray::makeArc(int x, int y, int w, int h, int a1, int a2)
+{
+ QRectF r(x, y, w, h);
+ QPointF startPoint;
+ qt_find_ellipse_coords(r, a1 / 16.0, a2 / 16.0, &startPoint, 0);
+
+ QPainterPath path(startPoint);
+ path.arcTo(r, a1 / 16.0, a2 / 16.0);
+
+ if (path.isEmpty())
+ *this = Q3PointArray();
+ else
+ *this = path.toSubpathPolygons().at(0).toPolygon();
+}
+#endif
+
+#ifndef QT_NO_TRANSFORMATIONS
+/*!
+ \overload
+
+ Sets the points of the array to those describing an arc of an
+ ellipse with width \a w and height \a h and position (\a x, \a y),
+ starting from angle \a a1, and spanning angle by \a a2, and
+ transformed by the matrix \a xf. The resulting array has
+ sufficient resolution for pixel accuracy.
+
+ Angles are specified in 16ths of a degree, i.e. a full circle
+ equals 5760 (16 * 360). Positive values mean counter-clockwise,
+ whereas negative values mean the clockwise direction. Zero
+ degrees is at the 3 o'clock position.
+*/
+void Q3PointArray::makeArc(int x, int y, int w, int h, int a1, int a2, const QMatrix &xf)
+{
+ QRectF r(x, y, w, h);
+ QPointF startPoint;
+ qt_find_ellipse_coords(r, a1 / 16.0, a2 / 16.0, &startPoint, 0);
+
+ QPainterPath path(startPoint);
+ path.arcTo(r, a1 / 16.0, a2 / 16.0);
+ path = path * xf;
+ if (path.isEmpty())
+ *this = Q3PointArray();
+ else
+ *this = path.toSubpathPolygons().at(0).toPolygon();
+}
+
+#endif // QT_NO_TRANSFORMATIONS
+
+/*!
+ \fn Q3PointArray::Q3PointArray()
+
+ Constructs an empty Q3PointArray.
+*/
+
+/*!
+ \fn Q3PointArray::Q3PointArray(const QRect &r, bool closed)
+
+ Constructs a point array from the rectangle \a r.
+
+ If \a closed is false, then the point array just contains the
+ following four points of the rectangle ordered clockwise. The
+ bottom-right point is located at (r.x() + r.width(), r.y() +
+ r.height()).
+*/
+
+/*!
+ \fn Q3PointArray::Q3PointArray(const QPolygon& other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ \fn Q3PointArray Q3PointArray::copy() const
+
+ Returns a copy of this Q3PointArray.
+*/
+
+/*!
+ \fn bool Q3PointArray::isNull()
+
+ Returns isEmpty(). Use isEmpty() instead.
+*/
+
+/*!
+ Sets the points of the array to those describing an ellipse with
+ size, width \a w by height \a h, and position (\a x, \a y).
+
+ The returned array has sufficient resolution for use as pixels.
+*/
+void Q3PointArray::makeEllipse(int x, int y, int w, int h)
+{
+ QPainterPath path;
+ path.addEllipse(x, y, w, h);
+ *this = path.toSubpathPolygons().at(0).toPolygon();
+}
+
+#ifndef QT_NO_BEZIER
+
+/*!
+ Returns the Bezier points for the four control points in this
+ array.
+*/
+Q3PointArray Q3PointArray::cubicBezier() const
+{
+ if (size() != 4) {
+ qWarning( "Q3PointArray::bezier: The array must have 4 control points" );
+ return QPolygon();
+ }
+ QPolygonF polygon = QBezier::fromPoints(at(0), at(1), at(2), at(3)).toPolygon();
+ return polygon.toPolygon();
+}
+#endif //QT_NO_BEZIER
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/painting/q3pointarray.h b/src/qt3support/painting/q3pointarray.h
new file mode 100644
index 0000000..35e5dae
--- /dev/null
+++ b/src/qt3support/painting/q3pointarray.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3POINTARRAY_H
+#define Q3POINTARRAY_H
+
+#include <QtGui/qpolygon.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3PointArray : public QPolygon
+{
+public:
+ inline Q3PointArray() : QPolygon() {}
+ inline Q3PointArray(const QRect &r, bool closed=false) : QPolygon(r, closed) {}
+ inline Q3PointArray(const QPolygon& a) : QPolygon(a) {}
+
+ inline Q3PointArray copy() const { return *this; }
+ inline bool isNull() { return isEmpty(); }
+ void makeEllipse(int x, int y, int w, int h);
+#ifndef QT_NO_WMATRIX
+ void makeArc(int x, int y, int w, int h, int a1, int a2);
+ void makeArc(int x, int y, int w, int h, int a1, int a2, const QMatrix &matrix);
+#endif
+ Q3PointArray cubicBezier() const;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3POINTARRAY_H
diff --git a/src/qt3support/qt3support.pro b/src/qt3support/qt3support.pro
new file mode 100644
index 0000000..1e0717d
--- /dev/null
+++ b/src/qt3support/qt3support.pro
@@ -0,0 +1,39 @@
+TARGET = Qt3Support
+QPRO_PWD = $$PWD
+QT = core gui network sql
+DEFINES += QT_BUILD_COMPAT_LIB
+DEFINES += QT_NO_USING_NAMESPACE
+win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x60000000
+
+include(../qbase.pri)
+DEFINES -= QT_ASCII_CAST_WARNINGS
+
+PRECOMPILED_HEADER = other/qt_compat_pch.h
+
+include(tools/tools.pri)
+include(sql/sql.pri)
+include(other/other.pri)
+include(itemviews/itemviews.pri)
+include(widgets/widgets.pri)
+include(dialogs/dialogs.pri)
+include(text/text.pri)
+include(canvas/canvas.pri)
+include(network/network.pri)
+include(painting/painting.pri)
+
+unix|win32-g++* {
+ QMAKE_PKGCONFIG_CFLAGS += -DQT3_SUPPORT
+ QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui QtNetwork QtSql
+}
+mac:LIBS_PRIVATE += -framework Carbon
+
+QMAKE_LIBS += $$QMAKE_LIBS_COMPAT $$QMAKE_LIBS_NETWORK
+DEFINES -= QT3_SUPPORT_WARNINGS
+DEFINES += QT3_SUPPORT
+MOCDIR = .moc
+
+*-g++*: QMAKE_CXXFLAGS += -fno-strict-aliasing
+
+CONFIG -= separate_debug_info
+CONFIG += no_debug_info
+
diff --git a/src/qt3support/sql/q3databrowser.cpp b/src/qt3support/sql/q3databrowser.cpp
new file mode 100644
index 0000000..b4f377e
--- /dev/null
+++ b/src/qt3support/sql/q3databrowser.cpp
@@ -0,0 +1,1281 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3databrowser.h"
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+#include "q3sqlform.h"
+#include "private/q3sqlmanager_p.h"
+#include "qsqlresult.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3DataBrowserPrivate
+{
+public:
+ Q3DataBrowserPrivate() : boundaryCheck(true), readOnly(false) {}
+ Q3SqlCursorManager cur;
+ Q3SqlFormManager frm;
+ Q3DataManager dat;
+ bool boundaryCheck;
+ bool readOnly;
+};
+
+/*!
+ \class Q3DataBrowser
+ \brief The Q3DataBrowser class provides data manipulation and
+ navigation for data entry forms.
+
+ \compat
+
+ A high-level API is provided for navigating through data records
+ in a cursor, for inserting, updating and deleting records, and for
+ refreshing data in the display.
+
+ If you want a read-only form to present database data use
+ Q3DataView; if you want a table-based presentation of your data use
+ Q3DataTable.
+
+ A Q3DataBrowser is used to associate a dataset with a form in much
+ the same way as a Q3DataTable associates a dataset with a table.
+ Once the data browser has been constructed it can be associated
+ with a dataset with setSqlCursor(), and with a form with
+ setForm(). Boundary checking, sorting and filtering can be set
+ with setBoundaryChecking(), setSort() and setFilter(),
+ respectively.
+
+ The insertCurrent() function reads the fields from the default
+ form into the default cursor and performs the insert. The
+ updateCurrent() and deleteCurrent() functions perform similarly to
+ update and delete the current record respectively.
+
+ The user can be asked to confirm all edits with setConfirmEdits().
+ For more precise control use setConfirmInsert(),
+ setConfirmUpdate(), setConfirmDelete() and setConfirmCancels().
+ Use setAutoEdit() to control the behavior of the form when the
+ user edits a record and then navigates.
+
+ The record set is navigated using first(), next(), prev(), last()
+ and seek(). The form's display is updated with refresh(). When
+ navigation takes place the firstRecordAvailable(),
+ lastRecordAvailable(), nextRecordAvailable() and
+ prevRecordAvailable() signals are emitted. When the cursor record
+ is changed due to navigation the cursorChanged() signal is
+ emitted.
+
+ If you want finer control of the insert, update and delete
+ processes then you can use the lower level functions to perform
+ these operations as described below.
+
+ The form is populated with data from the database with
+ readFields(). If the user is allowed to edit, (see setReadOnly()),
+ write the form's data back to the cursor's edit buffer with
+ writeFields(). You can clear the values in the form with
+ clearValues(). Editing is performed as follows:
+ \list
+ \i \e insert When the data browser enters insertion mode it emits the
+ primeInsert() signal which you can connect to, for example to
+ pre-populate fields. Call writeFields() to write the user's edits to
+ the cursor's edit buffer then call insert() to insert the record
+ into the database. The beforeInsert() signal is emitted just before
+ the cursor's edit buffer is inserted into the database; connect to
+ this for example, to populate fields such as an auto-generated
+ primary key.
+ \i \e update For updates the primeUpdate() signal is emitted when
+ the data browser enters update mode. After calling writeFields()
+ call update() to update the record and connect to the beforeUpdate()
+ signal to manipulate the user's data before the update takes place.
+ \i \e delete For deletion the primeDelete() signal is emitted when
+ the data browser enters deletion mode. After calling writeFields()
+ call del() to delete the record and connect to the beforeDelete()
+ signal, for example to record an audit of the deleted record.
+ \endlist
+
+*/
+
+/*!
+ \enum Q3DataBrowser::Boundary
+
+ This enum describes where the data browser is positioned.
+
+ \value Unknown the boundary cannot be determined (usually because
+ there is no default cursor, or the default cursor is not active).
+
+ \value None the browser is not positioned on a boundary, but it is
+ positioned on a record somewhere in the middle.
+
+ \value BeforeBeginning the browser is positioned before the
+ first available record.
+
+ \value Beginning the browser is positioned at the first record.
+
+ \value End the browser is positioned at the last
+ record.
+
+ \value AfterEnd the browser is positioned after the last
+ available record.
+*/
+
+/*!
+ Constructs a data browser which is a child of \a parent, with the
+ name \a name and widget flags set to \a fl.
+*/
+
+Q3DataBrowser::Q3DataBrowser(QWidget *parent, const char *name, Qt::WindowFlags fl)
+ : QWidget(parent, name, fl)
+{
+ d = new Q3DataBrowserPrivate();
+ d->dat.setMode(QSql::Update);
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3DataBrowser::~Q3DataBrowser()
+{
+ delete d;
+}
+
+
+/*!
+ Returns an enum indicating the boundary status of the browser.
+
+ This is achieved by moving the default cursor and checking the
+ position, however the current default form values will not be
+ altered. After checking for the boundary, the cursor is moved back
+ to its former position. See \l Q3DataBrowser::Boundary.
+
+ \sa Boundary
+*/
+
+Q3DataBrowser::Boundary Q3DataBrowser::boundary()
+{
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!cur || !cur->isActive())
+ return Unknown;
+ if (!cur->isValid()) {
+ if (cur->at() == QSql::BeforeFirst)
+ return BeforeBeginning;
+ if (cur->at() == QSql::AfterLast)
+ return AfterEnd;
+ return Unknown;
+ }
+ if (cur->at() == 0)
+ return Beginning;
+ int currentAt = cur->at();
+
+ Boundary b = None;
+ if (!cur->previous())
+ b = Beginning;
+ else
+ cur->seek(currentAt);
+ if (b == None && !cur->next())
+ b = End;
+ cur->seek(currentAt);
+ return b;
+}
+
+
+/*!
+ \property Q3DataBrowser::boundaryChecking
+ \brief whether boundary checking is active
+
+ When boundary checking is active (the default), signals are
+ emitted indicating the current position of the default cursor.
+
+ \sa boundary()
+*/
+
+void Q3DataBrowser::setBoundaryChecking(bool active)
+{
+ d->boundaryCheck = active;
+}
+
+bool Q3DataBrowser::boundaryChecking() const
+{
+ return d->boundaryCheck;
+}
+
+/*!
+ \property Q3DataBrowser::sort
+ \brief the data browser's sort
+
+ The data browser's sort affects the order in which records are
+ viewed in the browser. Call refresh() to apply the new sort.
+
+ When retrieving the sort property, a string list is returned in
+ the form 'fieldname order', e.g. 'id ASC', 'surname DESC'.
+
+ There is no default sort.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3databrowser.cpp 0
+*/
+
+void Q3DataBrowser::setSort(const QStringList& sort)
+{
+ d->cur.setSort(sort);
+}
+
+/*!
+ \overload
+
+ Sets the data browser's sort to the QSqlIndex \a sort. To apply
+ the new sort, use refresh().
+
+*/
+void Q3DataBrowser::setSort(const QSqlIndex& sort)
+{
+ d->cur.setSort(sort);
+}
+
+QStringList Q3DataBrowser::sort() const
+{
+ return d->cur.sort();
+}
+
+
+/*!
+ \property Q3DataBrowser::filter
+ \brief the data browser's filter
+
+ The filter applies to the data shown in the browser. Call
+ refresh() to apply the new filter. A filter is a string containing
+ a SQL WHERE clause without the WHERE keyword, e.g. "id>1000",
+ "name LIKE 'A%'", etc.
+
+ There is no default filter.
+
+ \sa sort()
+*/
+
+void Q3DataBrowser::setFilter(const QString& filter)
+{
+ d->cur.setFilter(filter);
+}
+
+
+QString Q3DataBrowser::filter() const
+{
+ return d->cur.filter();
+}
+
+
+/*!
+ Sets the default cursor used by the data browser to \a cursor. If
+ \a autoDelete is true (the default is false), the data browser
+ takes ownership of the \a cursor pointer, which will be deleted
+ when the browser is destroyed, or when setSqlCursor() is called
+ again. To activate the \a cursor use refresh(). The cursor's edit
+ buffer is used in the default form to browse and edit records.
+
+ \sa sqlCursor() form() setForm()
+*/
+
+void Q3DataBrowser::setSqlCursor(Q3SqlCursor* cursor, bool autoDelete)
+{
+ if (!cursor)
+ return;
+ d->cur.setCursor(cursor, autoDelete);
+ d->frm.setRecord(cursor->editBuffer());
+ if (cursor->isReadOnly())
+ setReadOnly(true);
+}
+
+
+/*!
+ Returns the default cursor used for navigation, or 0 if there is
+ no default cursor.
+
+ \sa setSqlCursor()
+*/
+
+Q3SqlCursor* Q3DataBrowser::sqlCursor() const
+{
+ return d->cur.cursor();
+}
+
+
+/*!
+ Sets the browser's default form to \a form. The cursor and all
+ navigation and data manipulation functions that the browser
+ provides become available to the \a form.
+*/
+
+void Q3DataBrowser::setForm(Q3SqlForm* form)
+{
+ d->frm.setForm(form);
+}
+
+
+/*!
+ Returns the data browser's default form or 0 if no form has been
+ set.
+*/
+
+Q3SqlForm* Q3DataBrowser::form()
+{
+ return d->frm.form();
+}
+
+/*!
+ \property Q3DataBrowser::readOnly
+ \brief whether the browser is read-only
+
+ The default is false, i.e. data can be edited. If the data browser
+ is read-only, no database edits will be allowed.
+*/
+
+void Q3DataBrowser::setReadOnly(bool active)
+{
+ d->readOnly = active;
+}
+
+bool Q3DataBrowser::isReadOnly() const
+{
+ return d->readOnly;
+}
+
+void Q3DataBrowser::setConfirmEdits(bool confirm)
+{
+ d->dat.setConfirmEdits(confirm);
+}
+
+/*!
+ \property Q3DataBrowser::confirmInsert
+ \brief whether the data browser confirms insertions
+
+ If this property is true, the browser confirms insertions,
+ otherwise insertions happen immediately.
+
+ \sa confirmCancels() confirmEdits() confirmUpdate() confirmDelete() confirmEdit()
+*/
+
+void Q3DataBrowser::setConfirmInsert(bool confirm)
+{
+ d->dat.setConfirmInsert(confirm);
+}
+
+/*!
+ \property Q3DataBrowser::confirmUpdate
+ \brief whether the browser confirms updates
+
+ If this property is true, the browser confirms updates, otherwise
+ updates happen immediately.
+
+ \sa confirmCancels() confirmEdits() confirmInsert() confirmDelete() confirmEdit()
+*/
+
+void Q3DataBrowser::setConfirmUpdate(bool confirm)
+{
+ d->dat.setConfirmUpdate(confirm);
+}
+
+/*!
+ \property Q3DataBrowser::confirmDelete
+ \brief whether the browser confirms deletions
+
+ If this property is true, the browser confirms deletions,
+ otherwise deletions happen immediately.
+
+ \sa confirmCancels() confirmEdits() confirmUpdate() confirmInsert() confirmEdit()
+*/
+
+void Q3DataBrowser::setConfirmDelete(bool confirm)
+{
+ d->dat.setConfirmDelete(confirm);
+}
+
+/*!
+ \property Q3DataBrowser::confirmEdits
+ \brief whether the browser confirms edits
+
+ If this property is true, the browser confirms all edit operations
+ (insertions, updates and deletions), otherwise all edit operations
+ happen immediately. Confirmation is achieved by presenting the
+ user with a message box -- this behavior can be changed by
+ reimplementing the confirmEdit() function,
+
+ \sa confirmEdit() confirmCancels() confirmInsert() confirmUpdate() confirmDelete()
+*/
+
+bool Q3DataBrowser::confirmEdits() const
+{
+ return (d->dat.confirmEdits());
+}
+
+bool Q3DataBrowser::confirmInsert() const
+{
+ return (d->dat.confirmInsert());
+}
+
+bool Q3DataBrowser::confirmUpdate() const
+{
+ return (d->dat.confirmUpdate());
+}
+
+bool Q3DataBrowser::confirmDelete() const
+{
+ return (d->dat.confirmDelete());
+}
+
+/*!
+ \property Q3DataBrowser::confirmCancels
+ \brief whether the browser confirms cancel operations
+
+ If this property is true, all cancels must be confirmed by the
+ user through a message box (this behavior can be changed by
+ overriding the confirmCancel() function), otherwise all cancels
+ occur immediately. The default is false.
+
+ \sa confirmEdits() confirmCancel()
+*/
+
+void Q3DataBrowser::setConfirmCancels(bool confirm)
+{
+ d->dat.setConfirmCancels(confirm);
+}
+
+bool Q3DataBrowser::confirmCancels() const
+{
+ return d->dat.confirmCancels();
+}
+
+/*!
+ \property Q3DataBrowser::autoEdit
+ \brief whether the browser automatically applies edits
+
+ The default value for this property is true. When the user begins
+ an insertion or an update on a form there are two possible
+ outcomes when they navigate to another record:
+
+ \list
+ \i the insert or update is is performed -- this occurs if autoEdit is true
+ \i the insert or update is discarded -- this occurs if autoEdit is false
+ \endlist
+*/
+
+void Q3DataBrowser::setAutoEdit(bool autoEdit)
+{
+ d->dat.setAutoEdit(autoEdit);
+}
+
+bool Q3DataBrowser::autoEdit() const
+{
+ return d->dat.autoEdit();
+}
+
+/*!
+ \fn void Q3DataBrowser::firstRecordAvailable(bool available)
+
+ This signal is emitted whenever the position of the cursor
+ changes. The \a available parameter indicates whether or not the
+ first record in the default cursor is available.
+*/
+
+/*!
+ \fn void Q3DataBrowser::lastRecordAvailable(bool available)
+
+ This signal is emitted whenever the position of the cursor
+ changes. The \a available parameter indicates whether or not the
+ last record in the default cursor is available.
+*/
+
+/*!
+ \fn void Q3DataBrowser::nextRecordAvailable(bool available)
+
+ This signal is emitted whenever the position of the cursor
+ changes. The \a available parameter indicates whether or not the
+ next record in the default cursor is available.
+*/
+
+
+/*!
+ \fn void Q3DataBrowser::prevRecordAvailable(bool available)
+
+ This signal is emitted whenever the position of the cursor
+ changes. The \a available parameter indicates whether or not the
+ previous record in the default cursor is available.
+*/
+
+
+/*!
+ \fn void Q3DataBrowser::currentChanged(const QSqlRecord* record)
+
+ This signal is emitted whenever the current cursor position
+ changes. The \a record parameter points to the contents of the
+ current cursor's record.
+*/
+
+
+/*!
+ \fn void Q3DataBrowser::primeInsert(QSqlRecord* buf)
+
+ This signal is emitted when the data browser enters insertion
+ mode. The \a buf parameter points to the record buffer that is to
+ be inserted. Connect to this signal to, for example, prime the
+ record buffer with default data values, auto-numbered fields etc.
+ (Note that Q3SqlCursor::primeInsert() is \e not called on the
+ default cursor, as this would corrupt values in the form.)
+
+ \sa insert()
+*/
+
+
+/*!
+ \fn void Q3DataBrowser::primeUpdate(QSqlRecord* buf)
+
+ This signal is emitted when the data browser enters update mode.
+ Note that during navigation (first(), last(), next(), prev()),
+ each record that is shown in the default form is primed for
+ update. The \a buf parameter points to the record buffer being
+ updated. (Note that Q3SqlCursor::primeUpdate() is \e not called on
+ the default cursor, as this would corrupt values in the form.)
+ Connect to this signal in order to, for example, keep track of
+ which records have been updated, perhaps for auditing purposes.
+
+ \sa update()
+*/
+
+/*!
+ \fn void Q3DataBrowser::primeDelete(QSqlRecord* buf)
+
+ This signal is emitted when the data browser enters deletion mode.
+ The \a buf parameter points to the record buffer being deleted.
+ (Note that Q3SqlCursor::primeDelete() is \e not called on the
+ default cursor, as this would corrupt values in the form.)
+ Connect to this signal in order to, for example, save a copy of
+ the deleted record for auditing purposes.
+
+ \sa del()
+*/
+
+
+/*!
+ \fn void Q3DataBrowser::cursorChanged(Q3SqlCursor::Mode mode)
+
+ This signal is emitted whenever the cursor record was changed due
+ to navigation. The \a mode parameter is the edit that just took
+ place, e.g. Insert, Update or Delete. See \l Q3SqlCursor::Mode.
+*/
+
+
+/*!
+ Refreshes the data browser's data using the default cursor. The
+ browser's current filter and sort are applied if they have been
+ set.
+
+ \sa setFilter() setSort()
+*/
+
+void Q3DataBrowser::refresh()
+{
+ d->cur.refresh();
+}
+
+
+/*!
+ Performs an insert operation on the data browser's cursor. If
+ there is no default cursor or no default form, nothing happens.
+
+ If auto-editing is on (see setAutoEdit()), the following happens:
+
+ \list
+ \i If the browser is already actively inserting a record,
+ the current form's data is inserted into the database.
+ \i If the browser is not inserting a record, but the current record
+ was changed by the user, the record is updated in the database with
+ the current form's data (i.e. with the changes).
+ \endlist
+
+ If there is an error handling any of the above auto-edit actions,
+ handleError() is called and no insert or update is performed.
+
+ If no error occurred, or auto-editing is not enabled, the data browser
+ begins actively inserting a record into the database by performing the
+ following actions:
+
+ \list
+ \i The default cursor is primed for insert using Q3SqlCursor::primeInsert().
+ \i The primeInsert() signal is emitted.
+ \i The form is updated with the values in the default cursor's.
+ edit buffer so that the user can fill in the values to be inserted.
+ \endlist
+
+*/
+
+void Q3DataBrowser::insert()
+{
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return;
+ bool doIns = true;
+ QSql::Confirm conf = QSql::Yes;
+ switch (d->dat.mode()) {
+ case QSql::Insert:
+ if (autoEdit()) {
+ if (confirmInsert())
+ conf = confirmEdit(QSql::Insert);
+ switch (conf) {
+ case QSql::Yes:
+ insertCurrent();
+ break;
+ case QSql::No:
+ break;
+ case QSql::Cancel:
+ doIns = false;
+ break;
+ }
+ }
+ break;
+ default:
+ if (autoEdit() && currentEdited()) {
+ if (confirmUpdate())
+ conf = confirmEdit(QSql::Update);
+ switch (conf) {
+ case QSql::Yes:
+ updateCurrent();
+ break;
+ case QSql::No:
+ break;
+ case QSql::Cancel:
+ doIns = false;
+ break;
+ }
+ }
+ break;
+ }
+ if (doIns) {
+ d->dat.setMode(QSql::Insert);
+ sqlCursor()->primeInsert();
+ emit primeInsert(d->frm.record());
+ readFields();
+ }
+}
+
+
+/*!
+ Performs an update operation on the data browser's cursor.
+
+ If there is no default cursor or no default form, nothing happens.
+ Otherwise, the following happens:
+
+ If the data browser is actively inserting a record (see insert()),
+ that record is inserted into the database using insertCurrent().
+ Otherwise, the database is updated with the current form's data
+ using updateCurrent(). If there is an error handling either
+ action, handleError() is called.
+*/
+
+void Q3DataBrowser::update()
+{
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return;
+ QSql::Confirm conf = QSql::Yes;
+ switch (d->dat.mode()){
+ case QSql::Insert:
+ if (confirmInsert())
+ conf = confirmEdit(QSql::Insert);
+ switch (conf) {
+ case QSql::Yes:
+ if (insertCurrent())
+ d->dat.setMode(QSql::Update);
+ break;
+ case QSql::No:
+ d->dat.setMode(QSql::Update);
+ cur->editBuffer(true);
+ readFields();
+ break;
+ case QSql::Cancel:
+ break;
+ }
+ break;
+ default:
+ d->dat.setMode(QSql::Update);
+ if (confirmUpdate())
+ conf = confirmEdit(QSql::Update);
+ switch (conf) {
+ case QSql::Yes:
+ updateCurrent();
+ break;
+ case QSql::No:
+ case QSql::Cancel:
+ break;
+ }
+ break;
+ }
+}
+
+
+/*!
+ Performs a delete operation on the data browser's cursor. If there
+ is no default cursor or no default form, nothing happens.
+
+ Otherwise, the following happens:
+
+ The current form's record is deleted from the database, providing
+ that the data browser is not in insert mode. If the data browser
+ is actively inserting a record (see insert()), the insert action
+ is canceled, and the browser navigates to the last valid record
+ that was current. If there is an error, handleError() is called.
+*/
+
+void Q3DataBrowser::del()
+{
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return;
+ QSql::Confirm conf = QSql::Yes;
+ switch (d->dat.mode()){
+ case QSql::Insert:
+ if (confirmCancels())
+ conf = confirmCancel(QSql::Insert);
+ if (conf == QSql::Yes) {
+ cur->editBuffer(true); /* restore from cursor */
+ readFields();
+ d->dat.setMode(QSql::Update);
+ } else
+ d->dat.setMode(QSql::Insert);
+ break;
+ default:
+ if (confirmDelete())
+ conf = confirmEdit(QSql::Delete);
+ switch (conf) {
+ case QSql::Yes:
+ emit primeDelete(buf);
+ deleteCurrent();
+ break;
+ case QSql::No:
+ case QSql::Cancel:
+ break;
+ }
+ d->dat.setMode(QSql::Update);
+ break;
+ }
+}
+
+/*!
+ Moves the default cursor to the record specified by index \a i
+ and refreshes the default form to display that record. If there is
+ no default form or no default cursor, nothing happens. If
+ \a relative is true (the default is false), the cursor is moved
+ relative to its current position. If the data browser successfully
+ navigated to the desired record, the default cursor is primed for
+ update and the primeUpdate() signal is emitted.
+
+ If the browser is already positioned on the desired record nothing
+ happens. Returns false if there is no cursor. Otherwise returns
+ true.
+*/
+
+bool Q3DataBrowser::seek(int i, bool relative)
+{
+ int b = 0;
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!cur)
+ return false;
+ if (preNav())
+ b = cur->seek(i, relative);
+ postNav(b);
+ return b;
+}
+
+/*!
+ Moves the default cursor to the first record and refreshes the
+ default form to display this record. If there is no default form
+ or no default cursor, nothing happens. If the data browser
+ successfully navigated to the first record, the default cursor is
+ primed for update and the primeUpdate() signal is emitted.
+
+ If the browser is already positioned on the first record nothing
+ happens.
+
+*/
+
+void Q3DataBrowser::first()
+{
+ nav(&Q3SqlCursor::first);
+}
+
+
+/*!
+ Moves the default cursor to the last record and refreshes the
+ default form to display this record. If there is no default form
+ or no default cursor, nothing happens. If the data browser
+ successfully navigated to the last record, the default cursor is
+ primed for update and the primeUpdate() signal is emitted.
+
+ If the browser is already positioned on the last record nothing
+ happens.
+*/
+
+void Q3DataBrowser::last()
+{
+ nav(&Q3SqlCursor::last);
+}
+
+
+/*!
+ Moves the default cursor to the next record and refreshes the
+ default form to display this record. If there is no default form
+ or no default cursor, nothing happens. If the data browser
+ successfully navigated to the next record, the default cursor is
+ primed for update and the primeUpdate() signal is emitted.
+
+ If the browser is positioned on the last record nothing happens.
+*/
+
+void Q3DataBrowser::next()
+{
+ nav(&Q3SqlCursor::next);
+}
+
+
+/*!
+ Moves the default cursor to the previous record and refreshes the
+ default form to display this record. If there is no default form
+ or no default cursor, nothing happens. If the data browser
+ successfully navigated to the previous record, the default cursor
+ is primed for update and the primeUpdate() signal is emitted.
+
+ If the browser is positioned on the first record nothing happens.
+*/
+
+void Q3DataBrowser::prev()
+{
+ nav(&Q3SqlCursor::previous);
+}
+
+/*!
+ Reads the fields from the default cursor's edit buffer and
+ displays them in the form. If there is no default cursor or no
+ default form, nothing happens.
+*/
+
+void Q3DataBrowser::readFields()
+{
+ d->frm.readFields();
+}
+
+
+/*!
+ Writes the form's data to the default cursor's edit buffer. If
+ there is no default cursor or no default form, nothing happens.
+*/
+
+void Q3DataBrowser::writeFields()
+{
+ d->frm.writeFields();
+}
+
+
+/*!
+ Clears all the values in the form.
+
+ All the edit buffer field values are set to their 'zero state',
+ e.g. 0 for numeric fields and "" for string fields. Then the
+ widgets are updated using the property map. For example, a
+ combobox that is property-mapped to integers would scroll to the
+ first item. See the \l Q3SqlPropertyMap constructor for the default
+ mappings of widgets to properties.
+*/
+
+void Q3DataBrowser::clearValues()
+{
+ d->frm.clearValues();
+}
+
+/*!
+ Reads the fields from the default form into the default cursor and
+ performs an insert on the default cursor. If there is no default
+ form or no default cursor, nothing happens. If an error occurred
+ during the insert into the database, handleError() is called and
+ false is returned. If the insert was successful, the cursor is
+ refreshed and relocated to the newly inserted record, the
+ cursorChanged() signal is emitted, and true is returned.
+
+ \sa cursorChanged() sqlCursor() form() handleError()
+*/
+
+bool Q3DataBrowser::insertCurrent()
+{
+ if (isReadOnly())
+ return false;
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return false;
+ writeFields();
+ emit beforeInsert(buf);
+ int ar = cur->insert();
+ if (!ar || !cur->isActive()) {
+ handleError(cur->lastError());
+ refresh();
+ updateBoundary();
+ } else {
+ refresh();
+ d->cur.findBuffer(cur->primaryIndex());
+ updateBoundary();
+ cursorChanged(Q3SqlCursor::Insert);
+ return true;
+ }
+ return false;
+}
+
+
+/*!
+ Reads the fields from the default form into the default cursor and
+ performs an update on the default cursor. If there is no default
+ form or no default cursor, nothing happens. If an error occurred
+ during the update on the database, handleError() is called and
+ false is returned. If the update was successful, the cursor is
+ refreshed and relocated to the updated record, the cursorChanged()
+ signal is emitted, and true is returned.
+
+ \sa cursor() form() handleError()
+*/
+
+bool Q3DataBrowser::updateCurrent()
+{
+ if (isReadOnly())
+ return false;
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return false;
+ writeFields();
+ emit beforeUpdate(buf);
+ int ar = cur->update();
+ if (!ar || !cur->isActive()) {
+ handleError(cur->lastError());
+ refresh();
+ updateBoundary();
+ } else {
+ refresh();
+ d->cur.findBuffer(cur->primaryIndex());
+ updateBoundary();
+ cur->editBuffer(true);
+ cursorChanged(Q3SqlCursor::Update);
+ readFields();
+ return true;
+ }
+ return false;
+}
+
+
+/*!
+ Performs a delete on the default cursor using the values from the
+ default form and updates the default form. If there is no default
+ form or no default cursor, nothing happens. If the deletion was
+ successful, the cursor is repositioned to the nearest record and
+ true is returned. The nearest record is the next record if there
+ is one otherwise the previous record if there is one. If an error
+ occurred during the deletion from the database, handleError() is
+ called and false is returned.
+
+ \sa cursor() form() handleError()
+*/
+
+bool Q3DataBrowser::deleteCurrent()
+{
+ if (isReadOnly())
+ return false;
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return false;
+ writeFields();
+ int n = cur->at();
+ emit beforeDelete(buf);
+ int ar = cur->del();
+ if (ar) {
+ refresh();
+ updateBoundary();
+ cursorChanged(Q3SqlCursor::Delete);
+ if (!cur->seek(n))
+ last();
+ if (cur->isValid()) {
+ cur->editBuffer(true);
+ readFields();
+ } else {
+ clearValues();
+ }
+ return true;
+ } else {
+ if (!cur->isActive()) {
+ handleError(cur->lastError());
+ refresh();
+ updateBoundary();
+ }
+ }
+ return false;
+}
+
+
+/*!
+ Returns true if the form's edit buffer differs from the current
+ cursor buffer; otherwise returns false.
+*/
+
+bool Q3DataBrowser::currentEdited()
+{
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return false;
+ if (!cur->isActive() || !cur->isValid())
+ return false;
+ writeFields();
+ for (int i = 0; i < cur->count(); ++i) {
+ if (cur->value(i) != buf->value(i))
+ return true;
+ }
+ return false;
+}
+
+/*! \internal
+
+ Pre-navigation checking.
+*/
+
+bool Q3DataBrowser::preNav()
+{
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return false;
+
+ if (!isReadOnly() && autoEdit() && currentEdited()) {
+ bool ok = true;
+ QSql::Confirm conf = QSql::Yes;
+ switch (d->dat.mode()){
+ case QSql::Insert:
+ if (confirmInsert())
+ conf = confirmEdit(QSql::Insert);
+ switch (conf) {
+ case QSql::Yes:
+ ok = insertCurrent();
+ d->dat.setMode(QSql::Update);
+ break;
+ case QSql::No:
+ d->dat.setMode(QSql::Update);
+ break;
+ case QSql::Cancel:
+ return false;
+ }
+ break;
+ default:
+ if (confirmUpdate())
+ conf = confirmEdit(QSql::Update);
+ switch (conf) {
+ case QSql::Yes:
+ ok = updateCurrent();
+ break;
+ case QSql::No:
+ break;
+ case QSql::Cancel:
+ return false;
+ }
+ }
+ return ok;
+ }
+ return true;
+}
+
+/*! \internal
+
+ Handles post-navigation according to \a primeUpd.
+*/
+
+void Q3DataBrowser::postNav(bool primeUpd)
+{
+ if (primeUpd) {
+ QSqlRecord* buf = d->frm.record();
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!buf || !cur)
+ return;
+ currentChanged(cur);
+ cur->primeUpdate();
+ emit primeUpdate(buf);
+ readFields();
+ }
+ updateBoundary();
+}
+
+/*! \internal
+
+ Navigate default cursor according to \a nav. Handles autoEdit.
+
+*/
+void Q3DataBrowser::nav(Nav nav)
+{
+ int b = 0;
+ Q3SqlCursor* cur = d->cur.cursor();
+ if (!cur)
+ return;
+ if (preNav())
+ b = (cur->*nav)();
+ postNav(b);
+}
+
+/*!
+ If boundaryChecking() is true, checks the boundary of the current
+ default cursor and emits signals which indicate the position of
+ the cursor.
+*/
+
+void Q3DataBrowser::updateBoundary()
+{
+ if (d->boundaryCheck) {
+ Boundary bound = boundary();
+ switch (bound) {
+ case Unknown:
+ case None:
+ emit firstRecordAvailable(true);
+ emit prevRecordAvailable(true);
+ emit nextRecordAvailable(true);
+ emit lastRecordAvailable(true);
+ break;
+
+ case BeforeBeginning:
+ emit firstRecordAvailable(false);
+ emit prevRecordAvailable(false);
+ emit nextRecordAvailable(true);
+ emit lastRecordAvailable(true);
+ break;
+
+ case Beginning:
+ emit firstRecordAvailable(false);
+ emit prevRecordAvailable(false);
+ emit nextRecordAvailable(true);
+ emit lastRecordAvailable(true);
+ break;
+
+ case End:
+ emit firstRecordAvailable(true);
+ emit prevRecordAvailable(true);
+ emit nextRecordAvailable(false);
+ emit lastRecordAvailable(false);
+ break;
+
+ case AfterEnd:
+ emit firstRecordAvailable(true);
+ emit prevRecordAvailable(true);
+ emit nextRecordAvailable(false);
+ emit lastRecordAvailable(false);
+ break;
+ }
+ }
+}
+
+/*!
+ Virtual function which handles the error \a error. The default
+ implementation warns the user with a message box.
+*/
+
+void Q3DataBrowser::handleError(const QSqlError& error)
+{
+ d->dat.handleError(this, error);
+}
+
+/*!
+ Protected virtual function which returns a confirmation for an
+ edit of mode \a m. Derived classes can reimplement this function
+ and provide their own confirmation dialog. The default
+ implementation uses a message box which prompts the user to
+ confirm the edit action.
+*/
+
+QSql::Confirm Q3DataBrowser::confirmEdit(QSql::Op m)
+{
+ return d->dat.confirmEdit(this, m);
+}
+
+/*!
+ Protected virtual function which returns a confirmation for
+ canceling an edit mode \a m. Derived classes can reimplement this
+ function and provide their own confirmation dialog. The default
+ implementation uses a message box which prompts the user to
+ confirm the edit action.
+*/
+
+QSql::Confirm Q3DataBrowser::confirmCancel(QSql::Op m)
+{
+ return d->dat.confirmCancel(this, m);
+}
+
+/*!
+ \fn void Q3DataBrowser::beforeInsert(QSqlRecord* buf)
+
+ This signal is emitted just before the cursor's edit buffer is
+ inserted into the database. The \a buf parameter points to the
+ edit buffer being inserted. You might connect to this signal to
+ populate a generated primary key for example.
+*/
+
+/*!
+ \fn void Q3DataBrowser::beforeUpdate(QSqlRecord* buf)
+
+ This signal is emitted just before the cursor's edit buffer is
+ updated in the database. The \a buf parameter points to the edit
+ buffer being updated. You might connect to this signal to capture
+ some auditing information about the update.
+*/
+
+/*!
+ \fn void Q3DataBrowser::beforeDelete(QSqlRecord* buf)
+
+ This signal is emitted just before the cursor's edit buffer is
+ deleted from the database. The \a buf parameter points to the edit
+ buffer being deleted. You might connect to this signal to capture
+ some auditing information about the deletion.
+*/
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/sql/q3databrowser.h b/src/qt3support/sql/q3databrowser.h
new file mode 100644
index 0000000..4169b5d
--- /dev/null
+++ b/src/qt3support/sql/q3databrowser.h
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DATABROWSER_H
+#define Q3DATABROWSER_H
+
+#include <QtGui/qwidget.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtSql/qsql.h>
+#include <QtSql/qsqlindex.h>
+#include <Qt3Support/q3sqlcursor.h>
+#include <QtSql/qsqlerror.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+class Q3SqlForm;
+class Q3DataBrowserPrivate;
+
+class Q_COMPAT_EXPORT Q3DataBrowser : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(bool boundaryChecking READ boundaryChecking WRITE setBoundaryChecking)
+ Q_PROPERTY(QString filter READ filter WRITE setFilter)
+ Q_PROPERTY(QStringList sort READ sort WRITE setSort)
+ Q_PROPERTY(bool confirmEdits READ confirmEdits WRITE setConfirmEdits)
+ Q_PROPERTY(bool confirmInsert READ confirmInsert WRITE setConfirmInsert)
+ Q_PROPERTY(bool confirmUpdate READ confirmUpdate WRITE setConfirmUpdate)
+ Q_PROPERTY(bool confirmDelete READ confirmDelete WRITE setConfirmDelete)
+ Q_PROPERTY(bool confirmCancels READ confirmCancels WRITE setConfirmCancels)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(bool autoEdit READ autoEdit WRITE setAutoEdit)
+
+public:
+ Q3DataBrowser(QWidget* parent=0, const char* name=0, Qt::WindowFlags fl = 0);
+ ~Q3DataBrowser();
+
+ enum Boundary {
+ Unknown,
+ None,
+ BeforeBeginning,
+ Beginning,
+ End,
+ AfterEnd
+ };
+
+ Boundary boundary();
+ void setBoundaryChecking(bool active);
+ bool boundaryChecking() const;
+
+ void setSort(const QSqlIndex& sort);
+ void setSort(const QStringList& sort);
+ QStringList sort() const;
+ void setFilter(const QString& filter);
+ QString filter() const;
+ virtual void setSqlCursor(Q3SqlCursor* cursor, bool autoDelete = false);
+ Q3SqlCursor* sqlCursor() const;
+ virtual void setForm(Q3SqlForm* form);
+ Q3SqlForm* form();
+
+ virtual void setConfirmEdits(bool confirm);
+ virtual void setConfirmInsert(bool confirm);
+ virtual void setConfirmUpdate(bool confirm);
+ virtual void setConfirmDelete(bool confirm);
+ virtual void setConfirmCancels(bool confirm);
+ bool confirmEdits() const;
+ bool confirmInsert() const;
+ bool confirmUpdate() const;
+ bool confirmDelete() const;
+ bool confirmCancels() const;
+
+ virtual void setReadOnly(bool active);
+ bool isReadOnly() const;
+ virtual void setAutoEdit(bool autoEdit);
+ bool autoEdit() const;
+
+ virtual bool seek(int i, bool relative = false);
+
+Q_SIGNALS:
+ void firstRecordAvailable(bool available);
+ void lastRecordAvailable(bool available);
+ void nextRecordAvailable(bool available);
+ void prevRecordAvailable(bool available);
+
+ void currentChanged(const QSqlRecord* record);
+ void primeInsert(QSqlRecord* buf);
+ void primeUpdate(QSqlRecord* buf);
+ void primeDelete(QSqlRecord* buf);
+ void beforeInsert(QSqlRecord* buf);
+ void beforeUpdate(QSqlRecord* buf);
+ void beforeDelete(QSqlRecord* buf);
+ void cursorChanged(Q3SqlCursor::Mode mode);
+
+public Q_SLOTS:
+ virtual void refresh();
+
+ virtual void insert();
+ virtual void update();
+ virtual void del();
+
+ virtual void first();
+ virtual void last();
+ virtual void next();
+ virtual void prev();
+
+ virtual void readFields();
+ virtual void writeFields();
+ virtual void clearValues();
+
+ void updateBoundary();
+
+protected:
+ virtual bool insertCurrent();
+ virtual bool updateCurrent();
+ virtual bool deleteCurrent();
+ virtual bool currentEdited();
+
+ virtual QSql::Confirm confirmEdit(QSql::Op m);
+ virtual QSql::Confirm confirmCancel(QSql::Op m);
+
+ virtual void handleError(const QSqlError& error);
+
+private:
+ typedef bool (Q3SqlCursor::*Nav)();
+ bool preNav();
+ void postNav(bool primeUpd);
+ void nav(Nav nav);
+ Q3DataBrowserPrivate* d;
+
+ Q_DISABLE_COPY(Q3DataBrowser)
+};
+
+#endif // QT_NO_SQL_VIEW_WIDGETS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DATABROWSER_H
diff --git a/src/qt3support/sql/q3datatable.cpp b/src/qt3support/sql/q3datatable.cpp
new file mode 100644
index 0000000..a10500e
--- /dev/null
+++ b/src/qt3support/sql/q3datatable.cpp
@@ -0,0 +1,2333 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3datatable.h"
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+#include "qevent.h"
+#include "qsqldriver.h"
+#include "q3sqleditorfactory.h"
+#include "q3sqlpropertymap.h"
+#include "qapplication.h"
+#include "qlayout.h"
+#include "qpainter.h"
+#include "q3popupmenu.h"
+#include "q3valuelist.h"
+#include "q3sqlmanager_p.h"
+#include "qsqlfield.h"
+#include "qdatetime.h"
+#include "qcursor.h"
+#include "qtimer.h"
+#include "qpointer.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define QT_DEBUG_DATATABLE
+
+class Q3DataTablePrivate
+{
+public:
+ Q3DataTablePrivate()
+ : nullTxtChanged( false ),
+ haveAllRows( false ),
+ continuousEdit( false ),
+ editorFactory( 0 ),
+ propertyMap( 0 ),
+ datefmt( Qt::TextDate ),
+ editRow( -1 ),
+ editCol( -1 ),
+ insertRowLast( -1 ),
+ insertPreRows( -1 ),
+ editBuffer( 0 ),
+ cancelMode( false ),
+ cancelInsert( false ),
+ cancelUpdate( false ),
+ lastAt( -1 )
+ {}
+ ~Q3DataTablePrivate() { if ( propertyMap ) delete propertyMap; }
+
+ QString nullTxt;
+ bool nullTxtChanged;
+ typedef Q3ValueList< uint > ColIndex;
+ ColIndex colIndex;
+ bool haveAllRows;
+ bool continuousEdit;
+ Q3SqlEditorFactory* editorFactory;
+ Q3SqlPropertyMap* propertyMap;
+ QString trueTxt;
+ Qt::DateFormat datefmt;
+ QString falseTxt;
+ int editRow;
+ int editCol;
+ int insertRowLast;
+ QString insertHeaderLabelLast;
+ int insertPreRows;
+ QSqlRecord* editBuffer;
+ bool cancelMode;
+ bool cancelInsert;
+ bool cancelUpdate;
+ int lastAt;
+ QString ftr;
+ QStringList srt;
+ QStringList fld;
+ QStringList fldLabel;
+ Q3ValueList<int> fldWidth;
+ Q3ValueList<QIconSet> fldIcon;
+ Q3ValueList<bool> fldHidden;
+ Q3SqlCursorManager cur;
+ Q3DataManager dat;
+};
+
+#ifdef QT_DEBUG_DATATABLE
+void qt_debug_buffer( const QString& msg, QSqlRecord* cursor )
+{
+ qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ qDebug(msg);
+ for ( uint j = 0; j < cursor->count(); ++j ) {
+ qDebug(cursor->field(j)->name() + " type:" + QString(cursor->field(j)->value().typeName()) + " value:" + cursor->field(j)->value().toString() );
+ }
+}
+#endif
+
+/*!
+ \enum Q3DataTable::Refresh
+
+ This enum describes the refresh options.
+
+ \value RefreshData refresh the data, i.e. read it from the database
+ \value RefreshColumns refresh the list of fields, e.g. the column headings
+ \value RefreshAll refresh both the data and the list of fields
+*/
+
+
+/*!
+ \class Q3DataTable
+ \brief The Q3DataTable class provides a flexible SQL table widget that supports browsing and editing.
+
+ \compat
+
+ Q3DataTable supports various functions for presenting and editing
+ SQL data from a \l Q3SqlCursor in a table.
+
+ If you want a to present your data in a form use QDataBrowser, or
+ for read-only forms, use QDataView instead.
+
+ When displaying data, Q3DataTable only retrieves data for visible
+ rows. If the driver supports the 'query size' property the
+ Q3DataTable will have the correct number of rows and the vertical
+ scroll bar will accurately reflect the number of rows displayed in
+ proportion to the number of rows in the dataset. If the driver
+ does not support the 'query size' property, rows are dynamically
+ fetched from the database on an as-needed basis with the scroll bar
+ becoming more accurate as the user scrolls down through the
+ records. This allows extremely large queries to be displayed as
+ quickly as possible, with minimum memory usage.
+
+ Q3DataTable inherits Q3Table's API and extends it with functions to
+ sort and filter the data and sort columns. See setSqlCursor(),
+ setFilter(), setSort(), setSorting(), sortColumn() and refresh().
+
+ When displaying editable cursors, cell editing will be enabled.
+ (For more information on editable cursors, see \l Q3SqlCursor).
+ Q3DataTable can be used to modify existing data and to add new
+ records. When a user makes changes to a field in the table, the
+ cursor's edit buffer is used. The table will not send changes in
+ the edit buffer to the database until the user moves to a
+ different record in the grid or presses Enter. Cell editing is
+ initiated by pressing F2 (or right clicking and then clicking the
+ appropriate popup menu item) and canceled by pressing Esc. If
+ there is a problem updating or adding data, errors are handled
+ automatically (see handleError() to change this behavior). Note
+ that if autoEdit() is false navigating to another record will
+ cancel the insert or update.
+
+ The user can be asked to confirm all edits with setConfirmEdits().
+ For more precise control use setConfirmInsert(),
+ setConfirmUpdate(), setConfirmDelete() and setConfirmCancels().
+ Use setAutoEdit() to control the behavior of the table when the
+ user edits a record and then navigates. (Note that setAutoDelete()
+ is unrelated; it is used to set whether the Q3SqlCursor is deleted
+ when the table is deleted.)
+
+ Since the data table can perform edits, it must be able to
+ uniquely identify every record so that edits are correctly
+ applied. Because of this the underlying cursor must have a valid
+ primary index to ensure that a unique record is inserted, updated
+ or deleted within the database otherwise the database may be
+ changed to an inconsistent state.
+
+ Q3DataTable creates editors using the default \l Q3SqlEditorFactory.
+ Different editor factories can be used by calling
+ installEditorFactory(). A property map is used to map between the
+ cell's value and the editor. You can use your own property map
+ with installPropertyMap().
+
+ The contents of a cell is available as a QString with text() or as
+ a QVariant with value(). The current record is returned by
+ currentRecord(). Use the find() function to search for a string in
+ the table.
+
+ Editing actions can be applied programmatically. For example, the
+ insertCurrent() function reads the fields from the current record
+ into the cursor and performs the insert. The updateCurrent() and
+ deleteCurrent() functions perform similarly to update and delete
+ the current record respectively.
+
+ Columns in the table can be created automatically based on the
+ cursor (see setSqlCursor()). Columns can be manipulated manually
+ using addColumn(), removeColumn() and setColumn().
+
+ The table automatically copies many of the properties of the
+ cursor to format the display of data within cells (alignment,
+ visibility, etc.). The cursor can be changed with setSqlCursor().
+ The filter (see setFilter()) and sort defined within the table are
+ used instead of the filter and sort set on the cursor. For sorting
+ options see setSort(), sortColumn(), sortAscending() and
+ sortDescending(). Note that sorting operations will not behave as
+ expected if you are using a QSqlSelectCursor because it uses
+ user-defined SQL queries to obtain data.
+
+ The text used to represent NULL, true and false values can be
+ changed with setNullText(), setTrueText() and setFalseText()
+ respectively. You can change the appearance of cells by
+ reimplementing paintField().
+
+ Whenever a new row is selected in the table the currentChanged()
+ signal is emitted. The primeInsert() signal is emitted when an
+ insert is initiated. The primeUpdate() and primeDelete() signals
+ are emitted when update and deletion are initiated respectively.
+ Just before the database is updated a signal is emitted;
+ beforeInsert(), beforeUpdate() or beforeDelete() as appropriate.
+
+*/
+
+/*!
+ Constructs a data table which is a child of \a parent, called
+ name \a name.
+*/
+
+Q3DataTable::Q3DataTable ( QWidget * parent, const char * name )
+ : Q3Table( parent, name )
+{
+ init();
+}
+
+/*!
+ Constructs a data table which is a child of \a parent, called name
+ \a name using the cursor \a cursor.
+
+ If \a autoPopulate is true (the default is false), columns are
+ automatically created based upon the fields in the \a cursor
+ record. Note that \a autoPopulate only governs the creation of
+ columns; to load the cursor's data into the table use refresh().
+
+ If the \a cursor is read-only, the table also becomes read-only.
+ In addition, the table adopts the cursor's driver's definition for
+ representing NULL values as strings.
+*/
+
+Q3DataTable::Q3DataTable ( Q3SqlCursor* cursor, bool autoPopulate, QWidget * parent, const char * name )
+ : Q3Table( parent, name )
+{
+ init();
+ setSqlCursor( cursor, autoPopulate );
+}
+
+/*! \internal
+*/
+
+
+void Q3DataTable::init()
+{
+ d = new Q3DataTablePrivate();
+ setAutoEdit( true );
+ setSelectionMode( SingleRow );
+ setFocusStyle( FollowStyle );
+ d->trueTxt = tr( "True" );
+ d->falseTxt = tr( "False" );
+ d->datefmt = Qt::LocalDate;
+ reset();
+ connect( this, SIGNAL(selectionChanged()),
+ SLOT(updateCurrentSelection()));
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3DataTable::~Q3DataTable()
+{
+ delete d;
+}
+
+
+/*!
+ Adds the next column to be displayed using the field \a fieldName,
+ column label \a label, width \a width and iconset \a iconset.
+
+ If \a label is specified, it is used as the column's header label,
+ otherwise the field's display label is used when setSqlCursor() is
+ called. The \a iconset is used to set the icon used by the column
+ header; by default there is no icon.
+
+ \sa setSqlCursor() refresh()
+*/
+
+void Q3DataTable::addColumn( const QString& fieldName,
+ const QString& label,
+ int width,
+ const QIconSet& iconset )
+{
+ d->fld += fieldName;
+ d->fldLabel += label;
+ d->fldIcon += iconset;
+ d->fldWidth += width;
+ d->fldHidden += false;
+}
+
+/*!
+ Sets the \a col column to display using the field \a fieldName,
+ column label \a label, width \a width and iconset \a iconset.
+
+ If \a label is specified, it is used as the column's header label,
+ otherwise the field's display label is used when setSqlCursor() is
+ called. The \a iconset is used to set the icon used by the column
+ header; by default there is no icon.
+
+ \sa setSqlCursor() refresh()
+*/
+
+void Q3DataTable::setColumn( uint col, const QString& fieldName,
+ const QString& label,
+ int width,
+ const QIconSet& iconset )
+{
+ d->fld[col]= fieldName;
+ d->fldLabel[col] = label;
+ d->fldIcon[col] = iconset;
+ d->fldWidth[col] = width;
+ d->fldHidden[col] = false;
+}
+
+/*!
+ Removes column \a col from the list of columns to be displayed. If
+ \a col does not exist, nothing happens.
+
+ \sa QSqlField
+*/
+
+void Q3DataTable::removeColumn( int col )
+{
+ if ( d->fld.begin() + col != d->fld.end() ) {
+ d->fld.remove( d->fld.at( col ) );
+ d->fldLabel.remove( d->fldLabel.at( col ) );
+ d->fldIcon.remove( d->fldIcon.at( col ) );
+ d->fldWidth.remove( d->fldWidth.at( col ) );
+ d->fldHidden.remove( d->fldHidden.at( col ) );
+ }
+}
+
+/*!
+ Sets the column \a col to the width \a w. Note that unlike Q3Table
+ the Q3DataTable is not immediately redrawn, you must call
+ refresh(Q3DataTable::RefreshColumns)
+ yourself.
+
+ \sa refresh()
+*/
+void Q3DataTable::setColumnWidth( int col, int w )
+{
+ if ( d->fldWidth.at( col ) != d->fldWidth.end() ) {
+ d->fldWidth[col] = w;
+ }
+}
+
+/*!
+ Resizes column \a col so that the column width is wide enough to
+ display the widest item the column contains (including the column
+ label). If the table's Q3SqlCursor is not currently active, the
+ cursor will be refreshed before the column width is calculated. Be
+ aware that this function may be slow on tables that contain large
+ result sets.
+*/
+void Q3DataTable::adjustColumn( int col )
+{
+ Q3SqlCursor * cur = sqlCursor();
+ if ( !cur || cur->count() <= col )
+ return;
+ if ( !cur->isActive() ) {
+ d->cur.refresh();
+ }
+ int oldRow = currentRow();
+ int w = fontMetrics().width( horizontalHeader()->label( col ) + QLatin1Char('W') );
+ cur->seek( QSql::BeforeFirst );
+ while ( cur->next() ) {
+ w = qMax( w, fontMetrics().width( fieldToString( cur->fieldPtr( indexOf( col ) ) ) ) + 10 );
+ }
+ setColumnWidth( col, w );
+ cur->seek( oldRow );
+ refresh( RefreshColumns );
+}
+
+/*! \reimp
+*/
+void Q3DataTable::setColumnStretchable( int col, bool s )
+{
+ if ( numCols() == 0 ) {
+ refresh( RefreshColumns );
+ }
+ if ( numCols() > col ) {
+ Q3Table::setColumnStretchable( col, s );
+ }
+}
+
+QString Q3DataTable::filter() const
+{
+ return d->cur.filter();
+}
+
+/*!
+ \property Q3DataTable::filter
+ \brief the data filter for the data table
+
+ The filter applies to the data shown in the table. To view data
+ with a new filter, use refresh(). A filter string is an SQL WHERE
+ clause without the WHERE keyword.
+
+ There is no default filter.
+
+ \sa sort()
+
+*/
+
+void Q3DataTable::setFilter( const QString& filter )
+{
+ d->cur.setFilter( filter );
+}
+
+
+/*!
+ \property Q3DataTable::sort
+ \brief the data table's sort
+
+ The table's sort affects the order in which data records are
+ displayed in the table. To apply a sort, use refresh().
+
+ When examining the sort property, a string list is returned with
+ each item having the form 'fieldname order' (e.g., 'id ASC',
+ 'surname DESC').
+
+ There is no default sort.
+
+ Note that if you want to iterate over the sort list, you should
+ iterate over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3datatable.cpp 0
+
+ \sa filter() refresh()
+*/
+
+void Q3DataTable::setSort( const QStringList& sort )
+{
+ d->cur.setSort( sort );
+}
+
+/*!
+ \overload
+
+ Sets the sort to be applied to the displayed data to \a sort. If
+ there is no current cursor, nothing happens. A QSqlIndex contains
+ field names and their ordering (ASC or DESC); these are used to
+ compose the ORDER BY clause.
+
+ \sa sort()
+*/
+
+void Q3DataTable::setSort( const QSqlIndex& sort )
+{
+ d->cur.setSort( sort );
+}
+
+QStringList Q3DataTable::sort() const
+{
+ return d->cur.sort();
+}
+
+/*!
+ Returns the cursor used by the data table.
+*/
+
+Q3SqlCursor* Q3DataTable::sqlCursor() const
+{
+ return d->cur.cursor();
+}
+
+void Q3DataTable::setConfirmEdits( bool confirm )
+{
+ d->dat.setConfirmEdits( confirm );
+}
+
+void Q3DataTable::setConfirmInsert( bool confirm )
+{
+ d->dat.setConfirmInsert( confirm );
+}
+
+void Q3DataTable::setConfirmUpdate( bool confirm )
+{
+ d->dat.setConfirmUpdate( confirm );
+}
+
+void Q3DataTable::setConfirmDelete( bool confirm )
+{
+ d->dat.setConfirmDelete( confirm );
+}
+
+/*!
+ \property Q3DataTable::confirmEdits
+ \brief whether the data table confirms edit operations
+
+ If the confirmEdits property is true, the data table confirms all
+ edit operations (inserts, updates and deletes). Finer control of
+ edit confirmation can be achieved using \l confirmCancels, \l
+ confirmInsert, \l confirmUpdate and \l confirmDelete.
+
+ \sa confirmCancels() confirmInsert() confirmUpdate() confirmDelete()
+*/
+
+bool Q3DataTable::confirmEdits() const
+{
+ return ( d->dat.confirmEdits() );
+}
+
+/*!
+ \property Q3DataTable::confirmInsert
+ \brief whether the data table confirms insert operations
+
+ If the confirmInsert property is true, all insertions must be
+ confirmed by the user through a message box (this behavior can be
+ changed by overriding the confirmEdit() function), otherwise all
+ insert operations occur immediately.
+
+ \sa confirmCancels() confirmEdits() confirmUpdate() confirmDelete()
+*/
+
+bool Q3DataTable::confirmInsert() const
+{
+ return ( d->dat.confirmInsert() );
+}
+
+/*!
+ \property Q3DataTable::confirmUpdate
+ \brief whether the data table confirms update operations
+
+ If the confirmUpdate property is true, all updates must be
+ confirmed by the user through a message box (this behavior can be
+ changed by overriding the confirmEdit() function), otherwise all
+ update operations occur immediately.
+
+ \sa confirmCancels() confirmEdits() confirmInsert() confirmDelete()
+*/
+
+bool Q3DataTable::confirmUpdate() const
+{
+ return ( d->dat.confirmUpdate() );
+}
+
+/*!
+ \property Q3DataTable::confirmDelete
+ \brief whether the data table confirms delete operations
+
+ If the confirmDelete property is true, all deletions must be
+ confirmed by the user through a message box (this behavior can be
+ changed by overriding the confirmEdit() function), otherwise all
+ delete operations occur immediately.
+
+ \sa confirmCancels() confirmEdits() confirmUpdate() confirmInsert()
+*/
+
+bool Q3DataTable::confirmDelete() const
+{
+ return ( d->dat.confirmDelete() );
+}
+
+/*!
+ \property Q3DataTable::confirmCancels
+ \brief whether the data table confirms cancel operations
+
+ If the confirmCancel property is true, all cancels must be
+ confirmed by the user through a message box (this behavior can be
+ changed by overriding the confirmCancel() function), otherwise all
+ cancels occur immediately. The default is false.
+
+ \sa confirmEdits() confirmCancel()
+*/
+
+void Q3DataTable::setConfirmCancels( bool confirm )
+{
+ d->dat.setConfirmCancels( confirm );
+}
+
+bool Q3DataTable::confirmCancels() const
+{
+ return d->dat.confirmCancels();
+}
+
+/*!
+ \reimp
+
+ For an editable table, creates an editor suitable for the field in
+ column \a col. The editor is created using the default editor
+ factory, unless a different editor factory was installed with
+ installEditorFactory(). The editor is primed with the value of the
+ field in \a col using a property map. The property map used is the
+ default property map, unless a new property map was installed with
+ installPropertMap(). If \a initFromCell is true then the editor is
+ primed with the value in the Q3DataTable cell.
+*/
+
+QWidget * Q3DataTable::createEditor( int , int col, bool initFromCell ) const
+{
+ if ( d->dat.mode() == QSql::None )
+ return 0;
+
+ Q3SqlEditorFactory * f = (d->editorFactory == 0) ?
+ Q3SqlEditorFactory::defaultFactory() : d->editorFactory;
+
+ Q3SqlPropertyMap * m = (d->propertyMap == 0) ?
+ Q3SqlPropertyMap::defaultMap() : d->propertyMap;
+
+ QWidget * w = 0;
+ if( initFromCell && d->editBuffer ){
+ w = f->createEditor( viewport(), d->editBuffer->fieldPtr( indexOf( col ) ) );
+ if ( w )
+ m->setProperty( w, d->editBuffer->value( indexOf( col ) ) );
+ }
+ return w;
+}
+
+/*! \reimp */
+bool Q3DataTable::eventFilter( QObject *o, QEvent *e )
+{
+ if ( d->cancelMode )
+ return true;
+
+ int r = currentRow();
+ int c = currentColumn();
+
+ if ( d->dat.mode() != QSql::None ) {
+ r = d->editRow;
+ c = d->editCol;
+ }
+
+ d->cancelInsert = false;
+ d->cancelUpdate = false;
+ switch ( e->type() ) {
+ case QEvent::KeyPress: {
+ int conf = QSql::Yes;
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if ( ( ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_BackTab )
+ && ke->state() & Qt::ControlButton )
+ return false;
+
+ if ( ke->key() == Qt::Key_Escape && d->dat.mode() == QSql::Insert ){
+ if ( confirmCancels() && !d->cancelMode ) {
+ d->cancelMode = true;
+ conf = confirmCancel( QSql::Insert );
+ d->cancelMode = false;
+ }
+ if ( conf == QSql::Yes ) {
+ d->cancelInsert = true;
+ } else {
+ QWidget *editorWidget = cellWidget( r, c );
+ if ( editorWidget ) {
+ editorWidget->setActiveWindow();
+ editorWidget->setFocus();
+ }
+ return true;
+ }
+ }
+ if ( ke->key() == Qt::Key_Escape && d->dat.mode() == QSql::Update ) {
+ if ( confirmCancels() && !d->cancelMode ) {
+ d->cancelMode = true;
+ conf = confirmCancel( QSql::Update );
+ d->cancelMode = false;
+ }
+ if ( conf == QSql::Yes ){
+ d->cancelUpdate = true;
+ } else {
+ QWidget *editorWidget = cellWidget( r, c );
+ if ( editorWidget ) {
+ editorWidget->setActiveWindow();
+ editorWidget->setFocus();
+ }
+ return true;
+ }
+ }
+ if ( ke->key() == Qt::Key_Insert && d->dat.mode() == QSql::None ) {
+ beginInsert();
+ return true;
+ }
+ if ( ke->key() == Qt::Key_Delete && d->dat.mode() == QSql::None ) {
+ deleteCurrent();
+ return true;
+ }
+ if ( d->dat.mode() != QSql::None ) {
+ if ( (ke->key() == Qt::Key_Tab) && (c < numCols() - 1) && (!isColumnReadOnly( c+1 ) || d->dat.mode() == QSql::Insert) )
+ d->continuousEdit = true;
+ else if ( (ke->key() == Qt::Key_BackTab) && (c > 0) && (!isColumnReadOnly( c-1 ) || d->dat.mode() == QSql::Insert) )
+ d->continuousEdit = true;
+ else
+ d->continuousEdit = false;
+ }
+ Q3SqlCursor * sql = sqlCursor();
+ if ( sql && sql->driver() &&
+ !sql->driver()->hasFeature( QSqlDriver::QuerySize ) &&
+ ke->key() == Qt::Key_End && d->dat.mode() == QSql::None ) {
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+#endif
+ int i = sql->at();
+ if ( i < 0 ) {
+ i = 0;
+ sql->seek(0);
+ }
+ while ( sql->next() )
+ i++;
+ setNumRows( i+1 );
+ setCurrentCell( i+1, currentColumn() );
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ return true;
+ }
+ break;
+ }
+ case QEvent::FocusOut: {
+ QWidget *editorWidget = cellWidget( r, c );
+ repaintCell( currentRow(), currentColumn() );
+ if ( !d->cancelMode && editorWidget && o == editorWidget &&
+ ( d->dat.mode() == QSql::Insert) && !d->continuousEdit) {
+ setCurrentCell( r, c );
+ d->cancelInsert = true;
+ }
+ d->continuousEdit = false;
+ break;
+ }
+ case QEvent::FocusIn:
+ repaintCell( currentRow(), currentColumn() );
+ break;
+ default:
+ break;
+ }
+ return Q3Table::eventFilter( o, e );
+}
+
+/*! \reimp */
+void Q3DataTable::resizeEvent ( QResizeEvent * e )
+{
+ if ( sqlCursor() &&
+ sqlCursor()->driver() &&
+ !sqlCursor()->driver()->hasFeature( QSqlDriver::QuerySize ) )
+ loadNextPage();
+ Q3Table::resizeEvent( e );
+}
+
+/*! \reimp */
+void Q3DataTable::contentsContextMenuEvent( QContextMenuEvent* e )
+{
+ Q3Table::contentsContextMenuEvent( e );
+ if ( isEditing() && d->dat.mode() != QSql::None )
+ endEdit( d->editRow, d->editCol, autoEdit(), false );
+ if ( !sqlCursor() )
+ return;
+ if ( d->dat.mode() == QSql::None ) {
+ if ( isReadOnly() )
+ return;
+ enum {
+ IdInsert,
+ IdUpdate,
+ IdDelete
+ };
+ QPointer<Q3PopupMenu> popup = new Q3PopupMenu( this, "qt_datatable_menu" );
+ int id[ 3 ];
+ id[ IdInsert ] = popup->insertItem( tr( "Insert" ) );
+ id[ IdUpdate ] = popup->insertItem( tr( "Update" ) );
+ id[ IdDelete ] = popup->insertItem( tr( "Delete" ) );
+ bool enableInsert = sqlCursor()->canInsert();
+ popup->setItemEnabled( id[ IdInsert ], enableInsert );
+ bool enableUpdate = currentRow() > -1 && sqlCursor()->canUpdate() && !isColumnReadOnly( currentColumn() );
+ popup->setItemEnabled( id[ IdUpdate ], enableUpdate );
+ bool enableDelete = currentRow() > -1 && sqlCursor()->canDelete();
+ popup->setItemEnabled( id[ IdDelete ], enableDelete );
+ int r = popup->exec( e->globalPos() );
+ delete (Q3PopupMenu*) popup;
+ if ( r == id[ IdInsert ] )
+ beginInsert();
+ else if ( r == id[ IdUpdate ] ) {
+ if ( beginEdit( currentRow(), currentColumn(), false ) )
+ setEditMode( Editing, currentRow(), currentColumn() );
+ else
+ endUpdate();
+ }
+ else if ( r == id[ IdDelete ] )
+ deleteCurrent();
+ e->accept();
+ }
+}
+
+/*! \reimp */
+void Q3DataTable::contentsMousePressEvent( QMouseEvent* e )
+{
+ Q3Table::contentsMousePressEvent( e );
+}
+
+/*! \reimp */
+QWidget* Q3DataTable::beginEdit ( int row, int col, bool replace )
+{
+ d->editRow = -1;
+ d->editCol = -1;
+ if ( !sqlCursor() )
+ return 0;
+ if ( d->dat.mode() == QSql::Insert && !sqlCursor()->canInsert() )
+ return 0;
+ if ( d->dat.mode() == QSql::Update && !sqlCursor()->canUpdate() )
+ return 0;
+ d->editRow = row;
+ d->editCol = col;
+ if ( d->continuousEdit ) {
+ // see comment in beginInsert()
+ bool fakeReadOnly = isColumnReadOnly( col );
+ setColumnReadOnly( col, false );
+ QWidget* w = Q3Table::beginEdit( row, col, replace );
+ setColumnReadOnly( col, fakeReadOnly );
+ return w;
+ }
+ if ( d->dat.mode() == QSql::None && sqlCursor()->canUpdate() && sqlCursor()->primaryIndex().count() > 0 )
+ return beginUpdate( row, col, replace );
+ return 0;
+}
+
+/*! \reimp */
+void Q3DataTable::endEdit( int row, int col, bool, bool )
+{
+ bool accept = autoEdit() && !d->cancelInsert && !d->cancelUpdate;
+
+ QWidget *editor = cellWidget( row, col );
+ if ( !editor )
+ return;
+ if ( d->cancelMode )
+ return;
+ if ( d->dat.mode() != QSql::None && d->editBuffer ) {
+ Q3SqlPropertyMap * m = (d->propertyMap == 0) ?
+ Q3SqlPropertyMap::defaultMap() : d->propertyMap;
+ d->editBuffer->setValue( indexOf( col ), m->property( editor ) );
+ clearCellWidget( row, col );
+ if ( !d->continuousEdit ) {
+ switch ( d->dat.mode() ) {
+ case QSql::Insert:
+ if ( accept )
+ QTimer::singleShot( 0, this, SLOT(doInsertCurrent()) );
+ else
+ endInsert();
+ break;
+ case QSql::Update:
+ if ( accept )
+ QTimer::singleShot( 0, this, SLOT(doUpdateCurrent()) );
+ else
+ endUpdate();
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ setEditMode( NotEditing, -1, -1 );
+ }
+ if ( d->dat.mode() == QSql::None )
+ viewport()->setFocus();
+ updateCell( row, col );
+ emit valueChanged( row, col );
+}
+
+/*! \internal */
+void Q3DataTable::doInsertCurrent()
+{
+ insertCurrent();
+}
+
+/*! \internal */
+void Q3DataTable::doUpdateCurrent()
+{
+ updateCurrent();
+ if ( d->dat.mode() == QSql::None ) {
+ viewport()->setFocus();
+ }
+}
+
+/*! \reimp */
+void Q3DataTable::activateNextCell()
+{
+// if ( d->dat.mode() == QSql::None )
+// Q3Table::activateNextCell();
+}
+
+/*! \internal
+*/
+
+void Q3DataTable::endInsert()
+{
+ if ( d->dat.mode() != QSql::Insert )
+ return;
+ d->dat.setMode( QSql::None );
+ d->editBuffer = 0;
+ verticalHeader()->setLabel( d->editRow, QString::number( d->editRow +1 ) );
+ d->editRow = -1;
+ d->editCol = -1;
+ d->insertRowLast = -1;
+ d->insertHeaderLabelLast.clear();
+ setEditMode( NotEditing, -1, -1 );
+ setNumRows( d->insertPreRows );
+ d->insertPreRows = -1;
+ viewport()->setFocus();
+}
+
+/*! \internal
+ */
+void Q3DataTable::endUpdate()
+{
+ d->dat.setMode( QSql::None );
+ d->editBuffer = 0;
+ updateRow( d->editRow );
+ d->editRow = -1;
+ d->editCol = -1;
+ setEditMode( NotEditing, -1, -1 );
+}
+
+/*!
+ Protected virtual function called when editing is about to begin
+ on a new record. If the table is read-only, or if there's no cursor
+ or the cursor does not allow inserts, nothing happens and false
+ is returned. Otherwise returns true.
+
+ Editing takes place using the cursor's edit buffer(see
+ Q3SqlCursor::editBuffer()).
+
+ When editing begins, a new row is created in the table marked with
+ an asterisk '*' in the row's vertical header column, i.e. at the
+ left of the row.
+*/
+bool Q3DataTable::beginInsert()
+{
+ if ( !sqlCursor() || isReadOnly() || !numCols() )
+ return false;
+ if ( !sqlCursor()->canInsert() )
+ return false;
+ int i = 0;
+ int row = currentRow();
+
+ d->insertPreRows = numRows();
+ if ( row < 0 || numRows() < 1 )
+ row = 0;
+ setNumRows( d->insertPreRows + 1 );
+ setCurrentCell( row, 0 );
+ d->editBuffer = sqlCursor()->primeInsert();
+ emit primeInsert( d->editBuffer );
+ d->dat.setMode( QSql::Insert );
+ int lastRow = row;
+ int lastY = contentsY() + visibleHeight();
+ for ( i = row; i < numRows() ; ++i ) {
+ QRect cg = cellGeometry( i, 0 );
+ if ( (cg.y()+cg.height()) > lastY ) {
+ lastRow = i;
+ break;
+ }
+ }
+ if ( lastRow == row && ( numRows()-1 > row ) )
+ lastRow = numRows() - 1;
+ d->insertRowLast = lastRow;
+ d->insertHeaderLabelLast = verticalHeader()->label( d->insertRowLast );
+ verticalHeader()->setLabel( row, QString(QLatin1Char('*')) );
+ d->editRow = row;
+ // in the db world it's common to allow inserting new records
+ // into a table that has read-only columns - temporarily
+ // switch off read-only mode for such columns
+ bool fakeReadOnly = isColumnReadOnly( 0 );
+ setColumnReadOnly( 0, false );
+ if ( Q3Table::beginEdit( row, 0, false ) )
+ setEditMode( Editing, row, 0 );
+ setColumnReadOnly( 0, fakeReadOnly );
+ return true;
+}
+
+/*!
+ Protected virtual function called when editing is about to begin
+ on an existing row. If the table is read-only, or if there's no
+ cursor, nothing happens.
+
+ Editing takes place using the cursor's edit buffer (see
+ Q3SqlCursor::editBuffer()).
+
+ \a row and \a col refer to the row and column in the Q3DataTable.
+
+ (\a replace is provided for reimplementors and reflects the API of
+ Q3Table::beginEdit().)
+*/
+
+QWidget* Q3DataTable::beginUpdate ( int row, int col, bool replace )
+{
+ if ( !sqlCursor() || isReadOnly() || isColumnReadOnly( col ) )
+ return 0;
+ setCurrentCell( row, col );
+ d->dat.setMode( QSql::Update );
+ if ( sqlCursor()->seek( row ) ) {
+ d->editBuffer = sqlCursor()->primeUpdate();
+ sqlCursor()->seek( currentRow() );
+ emit primeUpdate( d->editBuffer );
+ return Q3Table::beginEdit( row, col, replace );
+ }
+ return 0;
+}
+
+/*!
+ For an editable table, issues an insert on the current cursor
+ using the values in the cursor's edit buffer. If there is no
+ current cursor or there is no current "insert" row, nothing
+ happens. If confirmEdits() or confirmInsert() is true,
+ confirmEdit() is called to confirm the insert. Returns true if the
+ insert succeeded; otherwise returns false.
+
+ The underlying cursor must have a valid primary index to ensure
+ that a unique record is inserted within the database otherwise the
+ database may be changed to an inconsistent state.
+*/
+
+bool Q3DataTable::insertCurrent()
+{
+ if ( d->dat.mode() != QSql::Insert || ! numCols() )
+ return false;
+ if ( !sqlCursor()->canInsert() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("Q3DataTable::insertCurrent: insert not allowed for %s",
+ sqlCursor()->name().latin1() );
+#endif
+ endInsert();
+ return false;
+ }
+ int b = 0;
+ int conf = QSql::Yes;
+ if ( confirmEdits() || confirmInsert() )
+ conf = confirmEdit( QSql::Insert );
+ switch ( conf ) {
+ case QSql::Yes: {
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::waitCursor );
+#endif
+ emit beforeInsert( d->editBuffer );
+ b = sqlCursor()->insert();
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) {
+ handleError( sqlCursor()->lastError() );
+ endInsert(); // cancel the insert if anything goes wrong
+ refresh();
+ } else {
+ endInsert();
+ refresh();
+ QSqlIndex idx = sqlCursor()->primaryIndex();
+ findBuffer( idx, d->lastAt );
+ repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), false );
+ emit cursorChanged( QSql::Insert );
+ }
+ break;
+ }
+ case QSql::No:
+ endInsert();
+ break;
+ case QSql::Cancel:
+ if ( Q3Table::beginEdit( currentRow(), currentColumn(), false ) )
+ setEditMode( Editing, currentRow(), currentColumn() );
+ break;
+ }
+ return ( b > 0 );
+}
+
+/*! \internal
+
+ Updates the row \a row.
+*/
+
+void Q3DataTable::updateRow( int row )
+{
+ for ( int i = 0; i < numCols(); ++i )
+ updateCell( row, i );
+}
+
+/*!
+ For an editable table, issues an update using the cursor's edit
+ buffer. If there is no current cursor or there is no current
+ selection, nothing happens. If confirmEdits() or confirmUpdate()
+ is true, confirmEdit() is called to confirm the update. Returns
+ true if the update succeeded; otherwise returns false.
+
+ The underlying cursor must have a valid primary index to ensure
+ that a unique record is updated within the database otherwise the
+ database may be changed to an inconsistent state.
+*/
+
+bool Q3DataTable::updateCurrent()
+{
+ if ( d->dat.mode() != QSql::Update )
+ return false;
+ if ( sqlCursor()->primaryIndex().count() == 0 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("Q3DataTable::updateCurrent: no primary index for %s",
+ sqlCursor()->name().latin1() );
+#endif
+ endUpdate();
+ return false;
+ }
+ if ( !sqlCursor()->canUpdate() ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("Q3DataTable::updateCurrent: updates not allowed for %s",
+ sqlCursor()->name().latin1() );
+#endif
+ endUpdate();
+ return false;
+ }
+ int b = 0;
+ int conf = QSql::Yes;
+ if ( confirmEdits() || confirmUpdate() )
+ conf = confirmEdit( QSql::Update );
+ switch ( conf ) {
+ case QSql::Yes: {
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::waitCursor );
+#endif
+ emit beforeUpdate( d->editBuffer );
+ b = sqlCursor()->update();
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) {
+ handleError( sqlCursor()->lastError() );
+ endUpdate();
+ refresh();
+ setCurrentCell( d->editRow, d->editCol );
+ if ( Q3Table::beginEdit( d->editRow, d->editCol, false ) )
+ setEditMode( Editing, d->editRow, d->editCol );
+ } else {
+ emit cursorChanged( QSql::Update );
+ refresh();
+ endUpdate();
+ }
+ break;
+ }
+ case QSql::No:
+ endUpdate();
+ setEditMode( NotEditing, -1, -1 );
+ break;
+ case QSql::Cancel:
+ setCurrentCell( d->editRow, d->editCol );
+ if ( Q3Table::beginEdit( d->editRow, d->editCol, false ) )
+ setEditMode( Editing, d->editRow, d->editCol );
+ break;
+ }
+ return ( b > 0 );
+}
+
+/*!
+ For an editable table, issues a delete on the current cursor's
+ primary index using the values of the currently selected row. If
+ there is no current cursor or there is no current selection,
+ nothing happens. If confirmEdits() or confirmDelete() is true,
+ confirmEdit() is called to confirm the delete. Returns true if the
+ delete succeeded; otherwise false.
+
+ The underlying cursor must have a valid primary index to ensure
+ that a unique record is deleted within the database otherwise the
+ database may be changed to an inconsistent state.
+*/
+
+bool Q3DataTable::deleteCurrent()
+{
+ if ( !sqlCursor() || isReadOnly() )
+ return false;
+ if ( sqlCursor()->primaryIndex().count() == 0 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning("Q3DataTable::deleteCurrent: no primary index %s",
+ sqlCursor()->name().latin1() );
+#endif
+ return false;
+ }
+ if ( !sqlCursor()->canDelete() )
+ return false;
+
+ int b = 0;
+ int conf = QSql::Yes;
+ if ( confirmEdits() || confirmDelete() )
+ conf = confirmEdit( QSql::Delete );
+
+ // Have to have this here - the confirmEdit() might pop up a
+ // dialog that causes a repaint which the cursor to the
+ // record it has to repaint.
+ if ( !sqlCursor()->seek( currentRow() ) )
+ return false;
+ switch ( conf ) {
+ case QSql::Yes:{
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::waitCursor );
+#endif
+ sqlCursor()->primeDelete();
+ emit primeDelete( sqlCursor()->editBuffer() );
+ emit beforeDelete( sqlCursor()->editBuffer() );
+ b = sqlCursor()->del();
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ if ( !b )
+ handleError( sqlCursor()->lastError() );
+ refresh();
+ emit cursorChanged( QSql::Delete );
+ setCurrentCell( currentRow(), currentColumn() );
+ repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), false );
+ verticalHeader()->repaint(); // get rid of trailing garbage
+ }
+ break;
+ case QSql::No:
+ setEditMode( NotEditing, -1, -1 );
+ break;
+ }
+ return ( b > 0 );
+}
+
+/*!
+ Protected virtual function which returns a confirmation for an
+ edit of mode \a m. Derived classes can reimplement this function
+ to provide their own confirmation dialog. The default
+ implementation uses a message box which prompts the user to
+ confirm the edit action.
+*/
+
+QSql::Confirm Q3DataTable::confirmEdit( QSql::Op m )
+{
+ return d->dat.confirmEdit( this, m );
+}
+
+/*!
+ Protected virtual function which returns a confirmation for
+ canceling an edit mode of \a m. Derived classes can reimplement
+ this function to provide their own cancel dialog. The default
+ implementation uses a message box which prompts the user to
+ confirm the cancel.
+*/
+
+QSql::Confirm Q3DataTable::confirmCancel( QSql::Op m )
+{
+ return d->dat.confirmCancel( this, m );
+}
+
+
+/*!
+ Searches the current cursor for a cell containing the string \a
+ str starting at the current cell and working forwards (or
+ backwards if \a backwards is true). If the string is found, the
+ cell containing the string is set as the current cell. If \a
+ caseSensitive is false the case of \a str will be ignored.
+
+ The search will wrap, i.e. if the first (or if backwards is true,
+ last) cell is reached without finding \a str the search will
+ continue until it reaches the starting cell. If \a str is not
+ found the search will fail and the current cell will remain
+ unchanged.
+*/
+void Q3DataTable::find( const QString & str, bool caseSensitive, bool backwards )
+{
+ if ( !sqlCursor() )
+ return;
+
+ Q3SqlCursor * r = sqlCursor();
+ QString tmp, text;
+ uint row = currentRow(), startRow = row,
+ col = backwards ? currentColumn() - 1 : currentColumn() + 1;
+ bool wrap = true, found = false;
+
+ if( str.isEmpty() || str.isNull() )
+ return;
+
+ if( !caseSensitive )
+ tmp = str.lower();
+ else
+ tmp = str;
+
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor( Qt::waitCursor );
+#endif
+ while( wrap ){
+ while( !found && r->seek( row ) ){
+ for( int i = col; backwards ? (i >= 0) : (i < (int) numCols());
+ backwards ? i-- : i++ )
+ {
+ text = r->value( indexOf( i ) ).toString();
+ if( !caseSensitive ){
+ text = text.lower();
+ }
+ if( text.contains( tmp ) ){
+ setCurrentCell( row, i );
+ col = i;
+ found = true;
+ }
+ }
+ if( !backwards ){
+ col = 0;
+ row++;
+ } else {
+ col = numCols() - 1;
+ row--;
+ }
+ }
+ if( !backwards ){
+ if( startRow != 0 ){
+ startRow = 0;
+ } else {
+ wrap = false;
+ }
+ r->first();
+ row = 0;
+ } else {
+ if( startRow != (uint) (numRows() - 1) ){
+ startRow = numRows() - 1;
+ } else {
+ wrap = false;
+ }
+ r->last();
+ row = numRows() - 1;
+ }
+ }
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+}
+
+
+/*!
+ Resets the table so that it displays no data.
+
+ \sa setSqlCursor()
+*/
+
+void Q3DataTable::reset()
+{
+ clearCellWidget( currentRow(), currentColumn() );
+ switch ( d->dat.mode() ) {
+ case QSql::Insert:
+ endInsert();
+ break;
+ case QSql::Update:
+ endUpdate();
+ break;
+ default:
+ break;
+ }
+ ensureVisible( 0, 0 );
+ verticalScrollBar()->setValue(0);
+ setNumRows(0);
+
+ d->haveAllRows = false;
+ d->continuousEdit = false;
+ d->dat.setMode( QSql::None );
+ d->editRow = -1;
+ d->editCol = -1;
+ d->insertRowLast = -1;
+ d->insertHeaderLabelLast.clear();
+ d->cancelMode = false;
+ d->lastAt = -1;
+ d->fld.clear();
+ d->fldLabel.clear();
+ d->fldWidth.clear();
+ d->fldIcon.clear();
+ d->fldHidden.clear();
+ if ( sorting() )
+ horizontalHeader()->setSortIndicator( -1 );
+}
+
+/*!
+ Returns the index of the field within the current SQL query that
+ is displayed in column \a i.
+*/
+
+int Q3DataTable::indexOf( uint i ) const
+{
+ Q3DataTablePrivate::ColIndex::ConstIterator it = d->colIndex.at( i );
+ if ( it != d->colIndex.end() )
+ return *it;
+ return -1;
+}
+
+/*!
+ Returns true if the table will automatically delete the cursor
+ specified by setSqlCursor(); otherwise returns false.
+*/
+
+bool Q3DataTable::autoDelete() const
+{
+ return d->cur.autoDelete();
+}
+
+/*!
+ Sets the cursor auto-delete flag to \a enable. If \a enable is
+ true, the table will automatically delete the cursor specified by
+ setSqlCursor(). If \a enable is false (the default), the cursor
+ will not be deleted.
+*/
+
+void Q3DataTable::setAutoDelete( bool enable )
+{
+ d->cur.setAutoDelete( enable );
+}
+
+/*!
+ \property Q3DataTable::autoEdit
+ \brief whether the data table automatically applies edits
+
+ The default value for this property is true. When the user begins
+ an insert or update in the table there are two possible outcomes
+ when they navigate to another record:
+
+ \list 1
+ \i the insert or update is is performed -- this occurs if autoEdit is true
+ \i the insert or update is abandoned -- this occurs if autoEdit is false
+ \endlist
+*/
+
+void Q3DataTable::setAutoEdit( bool autoEdit )
+{
+ d->dat.setAutoEdit( autoEdit );
+}
+
+bool Q3DataTable::autoEdit() const
+{
+ return d->dat.autoEdit();
+}
+
+/*!
+ \property Q3DataTable::nullText
+ \brief the text used to represent NULL values
+
+ The nullText property will be used to represent NULL values in the
+ table. The default value is provided by the cursor's driver.
+*/
+
+void Q3DataTable::setNullText( const QString& nullText )
+{
+ d->nullTxt = nullText;
+ d->nullTxtChanged = true;
+}
+
+QString Q3DataTable::nullText() const
+{
+ return d->nullTxt;
+}
+
+/*!
+ \property Q3DataTable::trueText
+ \brief the text used to represent true values
+
+ The trueText property will be used to represent NULL values in the
+ table. The default value is "True".
+*/
+
+void Q3DataTable::setTrueText( const QString& trueText )
+{
+ d->trueTxt = trueText;
+}
+
+QString Q3DataTable::trueText() const
+{
+ return d->trueTxt;
+}
+
+/*!
+ \property Q3DataTable::falseText
+ \brief the text used to represent false values
+
+ The falseText property will be used to represent NULL values in
+ the table. The default value is "False".
+*/
+
+void Q3DataTable::setFalseText( const QString& falseText )
+{
+ d->falseTxt = falseText;
+}
+
+QString Q3DataTable::falseText() const
+{
+ return d->falseTxt;
+}
+
+/*!
+ \property Q3DataTable::dateFormat
+ \brief the format used for displaying date/time values
+
+ The dateFormat property is used for displaying date/time values in
+ the table. The default value is Qt::LocalDate.
+*/
+
+void Q3DataTable::setDateFormat( const Qt::DateFormat f )
+{
+ d->datefmt = f;
+}
+
+Qt::DateFormat Q3DataTable::dateFormat() const
+{
+ return d->datefmt;
+}
+
+/*!
+ \property Q3DataTable::numRows
+
+ \brief the number of rows in the table
+*/
+
+int Q3DataTable::numRows() const
+{
+ return Q3Table::numRows();
+}
+
+/*!
+ \reimp
+
+ The number of rows in the table will be determined by the cursor
+ (see setSqlCursor()), so normally this function should never be
+ called. It is included for completeness.
+*/
+
+void Q3DataTable::setNumRows ( int r )
+{
+ Q3Table::setNumRows( r );
+}
+
+/*!
+ \reimp
+
+ The number of columns in the table will be determined
+ automatically (see addColumn()), so normally this function should
+ never be called. It is included for completeness.
+*/
+
+void Q3DataTable::setNumCols ( int r )
+{
+ Q3Table::setNumCols( r );
+}
+
+/*!
+ \property Q3DataTable::numCols
+
+ \brief the number of columns in the table
+*/
+
+int Q3DataTable::numCols() const
+{
+ return Q3Table::numCols();
+}
+
+/*!
+ Returns the text in cell \a row, \a col, or an empty string if the
+ cell is empty. If the cell's value is NULL then nullText() will be
+ returned. If the cell does not exist then an empty string is
+ returned.
+*/
+
+QString Q3DataTable::text ( int row, int col ) const
+{
+ if ( !sqlCursor() )
+ return QString();
+
+ QString s;
+ if ( sqlCursor()->seek( row ) )
+ s = sqlCursor()->value( indexOf( col ) ).toString();
+ sqlCursor()->seek( currentRow() );
+ return s;
+}
+
+/*!
+ Returns the value in cell \a row, \a col, or an invalid value if
+ the cell does not exist or has no value.
+*/
+
+QVariant Q3DataTable::value ( int row, int col ) const
+{
+ if ( !sqlCursor() )
+ return QVariant();
+
+ QVariant v;
+ if ( sqlCursor()->seek( row ) )
+ v = sqlCursor()->value( indexOf( col ) );
+ sqlCursor()->seek( currentRow() );
+ return v;
+}
+
+/*! \internal
+ Used to update the table when the size of the result set cannot be
+ determined - divide the result set into pages and load the pages as
+ the user moves around in the table.
+*/
+void Q3DataTable::loadNextPage()
+{
+ if ( d->haveAllRows )
+ return;
+ if ( !sqlCursor() )
+ return;
+ int pageSize = 0;
+ int lookAhead = 0;
+ if ( height() ) {
+ pageSize = (int)( height() * 2 / 20 );
+ lookAhead = pageSize / 2;
+ }
+ int startIdx = verticalScrollBar()->value() / 20;
+ int endIdx = startIdx + pageSize + lookAhead;
+ if ( endIdx < numRows() || endIdx < 0 )
+ return;
+
+ // check for empty result set
+ if ( sqlCursor()->at() == QSql::BeforeFirst && !sqlCursor()->next() ) {
+ d->haveAllRows = true;
+ return;
+ }
+
+ while ( endIdx > 0 && !sqlCursor()->seek( endIdx ) )
+ endIdx--;
+ if ( endIdx != ( startIdx + pageSize + lookAhead ) )
+ d->haveAllRows = true;
+ // small hack to prevent Q3Table from moving the view when a row
+ // is selected and the contents is resized
+ SelectionMode m = selectionMode();
+ clearSelection();
+ setSelectionMode( NoSelection );
+ setNumRows( endIdx ? endIdx + 1 : 0 );
+ sqlCursor()->seek( currentRow() );
+ setSelectionMode( m );
+}
+
+/*! \internal */
+void Q3DataTable::sliderPressed()
+{
+ disconnect( verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(loadNextPage()) );
+}
+
+/*! \internal */
+void Q3DataTable::sliderReleased()
+{
+ loadNextPage();
+ connect( verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(loadNextPage()) );
+}
+
+/*!
+ Sorts column \a col in ascending order if \a ascending is true
+ (the default); otherwise sorts in descending order.
+
+ The \a wholeRows parameter is ignored; Q3DataTable always sorts
+ whole rows by the specified column.
+*/
+
+void Q3DataTable::sortColumn ( int col, bool ascending,
+ bool )
+{
+ if ( sorting() ) {
+ if ( isEditing() && d->dat.mode() != QSql::None )
+ endEdit( d->editRow, d->editCol, autoEdit(), false );
+ if ( !sqlCursor() )
+ return;
+ QSqlIndex lastSort = sqlCursor()->sort();
+ QSqlIndex newSort( lastSort.cursorName(), QLatin1String("newSort") );
+ const QSqlField *field = sqlCursor()->fieldPtr( indexOf( col ) );
+ if ( field )
+ newSort.append( *field );
+ newSort.setDescending( 0, !ascending );
+ horizontalHeader()->setSortIndicator( col, ascending );
+ setSort( newSort );
+ refresh();
+ }
+}
+
+/*! \reimp */
+void Q3DataTable::columnClicked ( int col )
+{
+ if ( sorting() ) {
+ if ( !sqlCursor() )
+ return;
+ QSqlIndex lastSort = sqlCursor()->sort();
+ bool asc = true;
+ if ( lastSort.count() && lastSort.fieldPtr( 0 )->name() == sqlCursor()->fieldPtr( indexOf( col ) )->name() )
+ asc = lastSort.isDescending( 0 );
+ sortColumn( col, asc );
+ emit currentChanged( sqlCursor() );
+ }
+}
+
+/*!
+ Repaints the cell at \a row, \a col.
+*/
+void Q3DataTable::repaintCell( int row, int col )
+{
+ QRect cg = cellGeometry( row, col );
+ QRect re( QPoint( cg.x() - 2, cg.y() - 2 ),
+ QSize( cg.width() + 4, cg.height() + 4 ) );
+ repaintContents( re, false );
+}
+
+/*!
+ \reimp
+
+ This function renders the cell at \a row, \a col with the value of
+ the corresponding cursor field on the painter \a p. Depending on
+ the table's current edit mode, paintField() is called for the
+ appropriate cursor field. \a cr describes the cell coordinates in
+ the content coordinate system. If \a selected is true the cell has
+ been selected and would normally be rendered differently than an
+ unselected cell.
+*/
+
+void Q3DataTable::paintCell( QPainter * p, int row, int col, const QRect & cr,
+ bool selected, const QColorGroup &cg )
+{
+ Q3Table::paintCell( p, row, col, cr, selected, cg ); // empty cell
+
+ if ( !sqlCursor() )
+ return;
+
+ p->setPen( selected ? cg.highlightedText() : cg.text() );
+ if ( d->dat.mode() != QSql::None ) {
+ if ( row == d->editRow && d->editBuffer ) {
+ paintField( p, d->editBuffer->fieldPtr( indexOf( col ) ), cr,
+ selected );
+ } else if ( row > d->editRow && d->dat.mode() == QSql::Insert ) {
+ if ( sqlCursor()->seek( row - 1 ) )
+ paintField( p, sqlCursor()->fieldPtr( indexOf( col ) ), cr,
+ selected );
+ } else {
+ if ( sqlCursor()->seek( row ) )
+ paintField( p, sqlCursor()->fieldPtr( indexOf( col ) ), cr,
+ selected );
+ }
+ } else {
+ if ( sqlCursor()->seek( row ) )
+ paintField( p, sqlCursor()->fieldPtr( indexOf( col ) ), cr, selected );
+
+ }
+}
+
+
+/*!
+ Paints the \a field on the painter \a p. The painter has already
+ been translated to the appropriate cell's origin where the \a
+ field is to be rendered. \a cr describes the cell coordinates in
+ the content coordinate system. The \a selected parameter is
+ ignored.
+
+ If you want to draw custom field content you must reimplement
+ paintField() to do the custom drawing. The default implementation
+ renders the \a field value as text. If the field is NULL,
+ nullText() is displayed in the cell. If the field is Boolean,
+ trueText() or falseText() is displayed as appropriate.
+*/
+
+void Q3DataTable::paintField( QPainter * p, const QSqlField* field,
+ const QRect & cr, bool )
+{
+ if ( !field )
+ return;
+ p->drawText( 2,2, cr.width()-4, cr.height()-4, fieldAlignment( field ), fieldToString( field ) );
+}
+
+/*!
+ Returns the alignment for \a field.
+*/
+
+int Q3DataTable::fieldAlignment( const QSqlField* /*field*/ )
+{
+ return Qt::AlignLeft | Qt::AlignVCenter; //## Reggie: add alignment to Q3Table
+}
+
+
+/*!
+ If the cursor's \a sql driver supports query sizes, the number of
+ rows in the table is set to the size of the query. Otherwise, the
+ table dynamically resizes itself as it is scrolled. If \a sql is
+ not active, it is made active by issuing a select() on the cursor
+ using the \a sql cursor's current filter and current sort.
+*/
+
+void Q3DataTable::setSize( Q3SqlCursor* sql )
+{
+ // ### what are the connect/disconnect calls doing here!? move to refresh()
+ if ( sql->driver() && sql->driver()->hasFeature( QSqlDriver::QuerySize ) ) {
+ setVScrollBarMode( Auto );
+ disconnect( verticalScrollBar(), SIGNAL(sliderPressed()),
+ this, SLOT(sliderPressed()) );
+ disconnect( verticalScrollBar(), SIGNAL(sliderReleased()),
+ this, SLOT(sliderReleased()) );
+ disconnect( verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(loadNextPage()) );
+ if ( numRows() != sql->size() )
+ setNumRows( sql->size() );
+ } else {
+ setVScrollBarMode( AlwaysOn );
+ connect( verticalScrollBar(), SIGNAL(sliderPressed()),
+ this, SLOT(sliderPressed()) );
+ connect( verticalScrollBar(), SIGNAL(sliderReleased()),
+ this, SLOT(sliderReleased()) );
+ connect( verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(loadNextPage()) );
+ setNumRows(0);
+ loadNextPage();
+ }
+}
+
+/*!
+ Sets \a cursor as the data source for the table. To force the
+ display of the data from \a cursor, use refresh(). If \a
+ autoPopulate is true, columns are automatically created based upon
+ the fields in the \a cursor record. If \a autoDelete is true (the
+ default is false), the table will take ownership of the \a cursor
+ and delete it when appropriate. If the \a cursor is read-only, the
+ table becomes read-only. The table adopts the cursor's driver's
+ definition for representing NULL values as strings.
+
+ \sa refresh() setReadOnly() setAutoDelete() QSqlDriver::nullText()
+*/
+
+void Q3DataTable::setSqlCursor( Q3SqlCursor* cursor, bool autoPopulate, bool autoDelete )
+{
+ setUpdatesEnabled( false );
+ d->cur.setCursor( 0 );
+ if ( cursor ) {
+ d->cur.setCursor( cursor, autoDelete );
+ if ( autoPopulate ) {
+ d->fld.clear();
+ d->fldLabel.clear();
+ d->fldWidth.clear();
+ d->fldIcon.clear();
+ d->fldHidden.clear();
+ for ( int i = 0; i < sqlCursor()->count(); ++i ) {
+ addColumn( sqlCursor()->fieldPtr( i )->name(), sqlCursor()->fieldPtr( i )->name() );
+ setColumnReadOnly( i, sqlCursor()->fieldPtr( i )->isReadOnly() );
+ }
+ }
+ setReadOnly( sqlCursor()->isReadOnly() );
+ if ( sqlCursor()->driver() && !d->nullTxtChanged )
+ setNullText(sqlCursor()->driver()->nullText() );
+ setAutoDelete( autoDelete );
+ } else {
+ setNumRows( 0 );
+ setNumCols( 0 );
+ }
+ setUpdatesEnabled( true );
+}
+
+
+/*!
+ Protected virtual function which is called when an error \a e has
+ occurred on the current cursor(). The default implementation
+ displays a warning message to the user with information about the
+ error.
+*/
+void Q3DataTable::handleError( const QSqlError& e )
+{
+ d->dat.handleError( this, e );
+}
+
+/*! \reimp
+ */
+
+void Q3DataTable::keyPressEvent( QKeyEvent* e )
+{
+ switch( e->key() ) {
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Prior:
+ case Qt::Key_Next:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_F2:
+ case Qt::Key_Enter: case Qt::Key_Return:
+ case Qt::Key_Tab: case Qt::Key_BackTab:
+ Q3Table::keyPressEvent( e );
+ default:
+ return;
+ }
+}
+
+/*! \reimp
+*/
+
+void Q3DataTable::resizeData ( int )
+{
+
+}
+
+/*! \reimp
+*/
+
+Q3TableItem * Q3DataTable::item ( int, int ) const
+{
+ return 0;
+}
+
+/*! \reimp
+*/
+
+void Q3DataTable::setItem ( int , int , Q3TableItem * )
+{
+
+}
+
+/*! \reimp
+*/
+
+void Q3DataTable::clearCell ( int , int )
+{
+
+}
+
+/*! \reimp
+*/
+
+void Q3DataTable::setPixmap ( int , int , const QPixmap & )
+{
+
+}
+
+/*! \reimp */
+void Q3DataTable::takeItem ( Q3TableItem * )
+{
+
+}
+
+/*!
+ Installs a new SQL editor factory \a f. This enables the user to
+ create and instantiate their own editors for use in cell editing.
+ Note that Q3DataTable takes ownership of this pointer, and will
+ delete it when it is no longer needed or when
+ installEditorFactory() is called again.
+
+ \sa Q3SqlEditorFactory
+*/
+
+void Q3DataTable::installEditorFactory( Q3SqlEditorFactory * f )
+{
+ if( f ) {
+ delete d->editorFactory;
+ d->editorFactory = f;
+ }
+}
+
+/*!
+ Installs a new property map \a m. This enables the user to create
+ and instantiate their own property maps for use in cell editing.
+ Note that Q3DataTable takes ownership of this pointer, and will
+ delete it when it is no longer needed or when installPropertMap()
+ is called again.
+
+ \sa Q3SqlPropertyMap
+*/
+
+void Q3DataTable::installPropertyMap( Q3SqlPropertyMap* m )
+{
+ if ( m ) {
+ delete d->propertyMap;
+ d->propertyMap = m;
+ }
+}
+
+/*! \internal
+
+ Sets the current selection to \a row, \a col.
+*/
+
+void Q3DataTable::setCurrentSelection( int row, int )
+{
+ if ( !sqlCursor() )
+ return;
+ if ( row == d->lastAt )
+ return;
+ if ( !sqlCursor()->seek( row ) )
+ return;
+ d->lastAt = row;
+ emit currentChanged( sqlCursor() );
+}
+
+void Q3DataTable::updateCurrentSelection()
+{
+ setCurrentSelection( currentRow(), -1 );
+}
+
+/*!
+ Returns the currently selected record, or 0 if there is no current
+ selection. The table owns the pointer, so do \e not delete it or
+ otherwise modify it or the cursor it points to.
+*/
+
+QSqlRecord* Q3DataTable::currentRecord() const
+{
+ if ( !sqlCursor() || currentRow() < 0 )
+ return 0;
+ if ( !sqlCursor()->seek( currentRow() ) )
+ return 0;
+ return sqlCursor();
+}
+
+/*!
+ Sorts column \a col in ascending order.
+
+ \sa setSorting()
+*/
+
+void Q3DataTable::sortAscending( int col )
+{
+ sortColumn( col, true );
+}
+
+/*!
+ Sorts column \a col in descending order.
+
+ \sa setSorting()
+*/
+
+void Q3DataTable::sortDescending( int col )
+{
+ sortColumn( col, false );
+}
+
+/*!
+ \fn void Q3DataTable::refresh( Refresh mode )
+
+ Refreshes the table. If there is no currently defined cursor (see
+ setSqlCursor()), nothing happens. The \a mode parameter determines
+ which type of refresh will take place.
+
+ \sa Refresh setSqlCursor() addColumn()
+*/
+
+void Q3DataTable::refresh( Q3DataTable::Refresh mode )
+{
+ Q3SqlCursor* cur = sqlCursor();
+ if ( !cur )
+ return;
+ bool refreshData = ( (mode & RefreshData) == RefreshData );
+ bool refreshCol = ( (mode & RefreshColumns) == RefreshColumns );
+ if ( ( (mode & RefreshAll) == RefreshAll ) ) {
+ refreshData = true;
+ refreshCol = true;
+ }
+ if ( !refreshCol && d->fld.count() && numCols() == 0 )
+ refreshCol = true;
+ viewport()->setUpdatesEnabled( false );
+ d->haveAllRows = false;
+ if ( refreshData ) {
+ if ( !d->cur.refresh() && d->cur.cursor() ) {
+ handleError( d->cur.cursor()->lastError() );
+ }
+ d->lastAt = -1;
+ }
+ if ( refreshCol ) {
+ setNumCols( 0 );
+ d->colIndex.clear();
+ if ( d->fld.count() ) {
+ const QSqlField* field = 0;
+ int i;
+ int fpos = -1;
+ for ( i = 0; i < (int)d->fld.count(); ++i ) {
+ if ( cur->fieldPtr( i ) && cur->fieldPtr( i )->name() == d->fld[ i ] )
+ // if there is a field with the desired name on the desired position
+ // then we take that
+ fpos = i;
+ else
+ // otherwise we take the first field that matches the desired name
+ fpos = cur->position( d->fld[ i ] );
+ field = cur->fieldPtr( fpos );
+ if ( field && ( cur->isGenerated( fpos ) ||
+ cur->isCalculated( field->name() ) ) )
+ {
+ setNumCols( numCols() + 1 );
+ d->colIndex.append( fpos );
+ setColumnReadOnly( numCols()-1, field->isReadOnly() || isColumnReadOnly( numCols()-1 ) );
+ horizontalHeader()->setLabel( numCols()-1, d->fldIcon[ i ], d->fldLabel[ i ] );
+ if ( d->fldHidden[ i ] ) {
+ Q3Table::showColumn( i ); // ugly but necessary
+ Q3Table::hideColumn( i );
+ } else {
+ Q3Table::showColumn( i );
+ }
+ if ( d->fldWidth[ i ] > -1 )
+ Q3Table::setColumnWidth( i, d->fldWidth[i] );
+ }
+ }
+ }
+ }
+ viewport()->setUpdatesEnabled( true );
+ viewport()->repaint( false );
+ horizontalHeader()->repaint();
+ verticalHeader()->repaint();
+ setSize( cur );
+ // keep others aware
+ if ( d->lastAt == -1 )
+ setCurrentSelection( -1, -1 );
+ else if ( d->lastAt != currentRow() )
+ setCurrentSelection( currentRow(), currentColumn() );
+ if ( cur->isValid() )
+ emit currentChanged( sqlCursor() );
+}
+
+/*!
+ Refreshes the table. The cursor is refreshed using the current
+ filter, the current sort, and the currently defined columns.
+ Equivalent to calling refresh( Q3DataTable::RefreshData ).
+*/
+
+void Q3DataTable::refresh()
+{
+ refresh( RefreshData );
+}
+
+/*!
+ \internal
+
+ Selects the record in the table using the current cursor edit
+ buffer and the fields specified by the index \a idx. If \a atHint
+ is specified, it will be used as a hint about where to begin
+ searching.
+*/
+
+bool Q3DataTable::findBuffer( const QSqlIndex& idx, int atHint )
+{
+ Q3SqlCursor* cur = sqlCursor();
+ if ( !cur )
+ return false;
+ bool found = d->cur.findBuffer( idx, atHint );
+ if ( found )
+ setCurrentCell( cur->at(), currentColumn() );
+ return found;
+}
+
+/*! \internal
+ Returns the string representation of a database field.
+*/
+QString Q3DataTable::fieldToString( const QSqlField * field )
+{
+ QString text;
+ if ( field->isNull() ) {
+ text = nullText();
+ } else {
+ QVariant val = field->value();
+ switch ( val.type() ) {
+ case QVariant::Bool:
+ text = val.toBool() ? d->trueTxt : d->falseTxt;
+ break;
+ case QVariant::Date:
+ text = val.toDate().toString( d->datefmt );
+ break;
+ case QVariant::Time:
+ text = val.toTime().toString( d->datefmt );
+ break;
+ case QVariant::DateTime:
+ text = val.toDateTime().toString( d->datefmt );
+ break;
+ default:
+ text = val.toString();
+ break;
+ }
+ }
+ return text;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3DataTable::swapColumns( int col1, int col2, bool )
+{
+ QString fld = d->fld[ col1 ];
+ QString fldLabel = d->fldLabel[ col1 ];
+ QIconSet fldIcon = d->fldIcon[ col1 ];
+ int fldWidth = d->fldWidth[ col1 ];
+
+ d->fld[ col1 ] = d->fld[ col2 ];
+ d->fldLabel[ col1 ] = d->fldLabel[ col2 ];
+ d->fldIcon[ col1 ] = d->fldIcon[ col2 ];
+ d->fldWidth[ col1 ] = d->fldWidth[ col2 ];
+
+ d->fld[ col2 ] = fld;
+ d->fldLabel[ col2 ] = fldLabel;
+ d->fldIcon[ col2 ] = fldIcon;
+ d->fldWidth[ col2 ] = fldWidth;
+
+ int colIndex = d->colIndex[ col1 ];
+ d->colIndex[ col1 ] = d->colIndex[ col2 ];
+ d->colIndex[ col2 ] = colIndex;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3DataTable::drawContents( QPainter * p, int cx, int cy, int cw, int ch )
+{
+ Q3Table::drawContents( p, cx, cy, cw, ch );
+ if ( sqlCursor() && currentRow() >= 0 )
+ sqlCursor()->seek( currentRow() );
+}
+
+/*!
+ \reimp
+ */
+void Q3DataTable::drawContents(QPainter *)
+{
+}
+
+/*!
+ \reimp
+*/
+
+void Q3DataTable::hideColumn( int col )
+{
+ d->fldHidden[col] = true;
+ refresh( RefreshColumns );
+}
+
+/*!
+ \reimp
+*/
+
+void Q3DataTable::showColumn( int col )
+{
+ d->fldHidden[col] = false;
+ refresh( RefreshColumns );
+}
+
+/*!
+ \reimp
+*/
+void Q3DataTable::selectRow(int row)
+{
+ setCurrentCell(row, currentColumn());
+}
+
+/*!
+ \fn void Q3DataTable::currentChanged( QSqlRecord* record )
+
+ This signal is emitted whenever a new row is selected in the
+ table. The \a record parameter points to the contents of the newly
+ selected record.
+*/
+
+/*!
+ \fn void Q3DataTable::primeInsert( QSqlRecord* buf )
+
+ This signal is emitted after the cursor is primed for insert by
+ the table, when an insert action is beginning on the table. The \a
+ buf parameter points to the edit buffer being inserted. Connect to
+ this signal in order to, for example, prime the record buffer with
+ default data values.
+*/
+
+/*!
+ \fn void Q3DataTable::primeUpdate( QSqlRecord* buf )
+
+ This signal is emitted after the cursor is primed for update by
+ the table, when an update action is beginning on the table. The \a
+ buf parameter points to the edit buffer being updated. Connect to
+ this signal in order to, for example, provide some visual feedback
+ that the user is in 'edit mode'.
+*/
+
+/*!
+ \fn void Q3DataTable::primeDelete( QSqlRecord* buf )
+
+ This signal is emitted after the cursor is primed for delete by
+ the table, when a delete action is beginning on the table. The \a
+ buf parameter points to the edit buffer being deleted. Connect to
+ this signal in order to, for example, record auditing information
+ on deletions.
+*/
+
+/*!
+ \fn void Q3DataTable::beforeInsert( QSqlRecord* buf )
+
+ This signal is emitted just before the cursor's edit buffer is
+ inserted into the database. The \a buf parameter points to the
+ edit buffer being inserted. Connect to this signal to, for
+ example, populate a key field with a unique sequence number.
+*/
+
+/*!
+ \fn void Q3DataTable::beforeUpdate( QSqlRecord* buf )
+
+ This signal is emitted just before the cursor's edit buffer is
+ updated in the database. The \a buf parameter points to the edit
+ buffer being updated. Connect to this signal when you want to
+ transform the user's data behind-the-scenes.
+*/
+
+/*!
+ \fn void Q3DataTable::beforeDelete( QSqlRecord* buf )
+
+ This signal is emitted just before the currently selected record
+ is deleted from the database. The \a buf parameter points to the
+ edit buffer being deleted. Connect to this signal to, for example,
+ copy some of the fields for later use.
+*/
+
+/*!
+ \fn void Q3DataTable::cursorChanged( QSql::Op mode )
+
+ This signal is emitted whenever the cursor record was changed due
+ to an edit. The \a mode parameter is the type of edit that just
+ took place.
+*/
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/sql/q3datatable.h b/src/qt3support/sql/q3datatable.h
new file mode 100644
index 0000000..041795d
--- /dev/null
+++ b/src/qt3support/sql/q3datatable.h
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DATATABLE_H
+#define Q3DATATABLE_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qvariant.h>
+#include <Qt3Support/q3table.h>
+#include <QtSql/qsql.h>
+#include <Qt3Support/q3sqlcursor.h>
+#include <QtSql/qsqlindex.h>
+#include <Qt3Support/q3sqleditorfactory.h>
+#include <Qt3Support/qiconset.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+class QPainter;
+class QSqlField;
+class Q3SqlPropertyMap;
+class Q3DataTablePrivate;
+
+class Q_COMPAT_EXPORT Q3DataTable : public Q3Table
+{
+ Q_OBJECT
+
+ Q_PROPERTY( QString nullText READ nullText WRITE setNullText )
+ Q_PROPERTY( QString trueText READ trueText WRITE setTrueText )
+ Q_PROPERTY( QString falseText READ falseText WRITE setFalseText )
+ Q_PROPERTY( Qt::DateFormat dateFormat READ dateFormat WRITE setDateFormat )
+ Q_PROPERTY( bool confirmEdits READ confirmEdits WRITE setConfirmEdits )
+ Q_PROPERTY( bool confirmInsert READ confirmInsert WRITE setConfirmInsert )
+ Q_PROPERTY( bool confirmUpdate READ confirmUpdate WRITE setConfirmUpdate )
+ Q_PROPERTY( bool confirmDelete READ confirmDelete WRITE setConfirmDelete )
+ Q_PROPERTY( bool confirmCancels READ confirmCancels WRITE setConfirmCancels )
+ Q_PROPERTY( bool autoEdit READ autoEdit WRITE setAutoEdit )
+ Q_PROPERTY( QString filter READ filter WRITE setFilter )
+ Q_PROPERTY( QStringList sort READ sort WRITE setSort )
+ Q_PROPERTY( int numCols READ numCols )
+ Q_PROPERTY( int numRows READ numRows )
+
+public:
+ Q3DataTable ( QWidget* parent=0, const char* name=0 );
+ Q3DataTable ( Q3SqlCursor* cursor, bool autoPopulate = false, QWidget* parent=0, const char* name=0 );
+ ~Q3DataTable();
+
+ virtual void addColumn( const QString& fieldName,
+ const QString& label = QString(),
+ int width = -1,
+ const QIconSet& iconset = QIconSet() );
+ virtual void removeColumn( int col );
+ virtual void setColumn( uint col, const QString& fieldName,
+ const QString& label = QString(),
+ int width = -1,
+ const QIconSet& iconset = QIconSet() );
+
+ QString nullText() const;
+ QString trueText() const;
+ QString falseText() const;
+ Qt::DateFormat dateFormat() const;
+ bool confirmEdits() const;
+ bool confirmInsert() const;
+ bool confirmUpdate() const;
+ bool confirmDelete() const;
+ bool confirmCancels() const;
+ bool autoDelete() const;
+ bool autoEdit() const;
+ QString filter() const;
+ QStringList sort() const;
+
+ virtual void setSqlCursor( Q3SqlCursor* cursor = 0,
+ bool autoPopulate = false, bool autoDelete = false );
+ Q3SqlCursor* sqlCursor() const;
+
+ virtual void setNullText( const QString& nullText );
+ virtual void setTrueText( const QString& trueText );
+ virtual void setFalseText( const QString& falseText );
+ virtual void setDateFormat( const Qt::DateFormat f );
+ virtual void setConfirmEdits( bool confirm );
+ virtual void setConfirmInsert( bool confirm );
+ virtual void setConfirmUpdate( bool confirm );
+ virtual void setConfirmDelete( bool confirm );
+ virtual void setConfirmCancels( bool confirm );
+ virtual void setAutoDelete( bool enable );
+ virtual void setAutoEdit( bool autoEdit );
+ virtual void setFilter( const QString& filter );
+ virtual void setSort( const QStringList& sort );
+ virtual void setSort( const QSqlIndex& sort );
+
+ enum Refresh {
+ RefreshData = 1,
+ RefreshColumns = 2,
+ RefreshAll = 3
+ };
+ void refresh( Refresh mode );
+ void sortColumn ( int col, bool ascending = true,
+ bool wholeRows = false );
+ QString text ( int row, int col ) const;
+ QVariant value ( int row, int col ) const;
+ QSqlRecord* currentRecord() const;
+
+ void installEditorFactory( Q3SqlEditorFactory * f );
+ void installPropertyMap( Q3SqlPropertyMap* m );
+
+ int numCols() const;
+ int numRows() const;
+ void setNumCols( int c );
+ void setNumRows ( int r );
+ bool findBuffer( const QSqlIndex& idx, int atHint = 0 );
+
+ void hideColumn( int col );
+ void showColumn( int col );
+ int indexOf( uint i ) const;
+ void selectRow(int row);
+
+Q_SIGNALS:
+ void currentChanged( QSqlRecord* record );
+ void primeInsert( QSqlRecord* buf );
+ void primeUpdate( QSqlRecord* buf );
+ void primeDelete( QSqlRecord* buf );
+ void beforeInsert( QSqlRecord* buf );
+ void beforeUpdate( QSqlRecord* buf );
+ void beforeDelete( QSqlRecord* buf );
+ void cursorChanged( QSql::Op mode );
+
+public Q_SLOTS:
+ virtual void find( const QString & str, bool caseSensitive,
+ bool backwards );
+ virtual void sortAscending( int col );
+ virtual void sortDescending( int col );
+ virtual void refresh();
+ void setColumnWidth( int col, int w );
+ void adjustColumn( int col );
+ void setColumnStretchable( int col, bool stretch );
+ void swapColumns( int col1, int col2, bool swapHeaders = false );
+
+protected:
+ virtual bool insertCurrent();
+ virtual bool updateCurrent();
+ virtual bool deleteCurrent();
+
+ virtual QSql::Confirm confirmEdit( QSql::Op m );
+ virtual QSql::Confirm confirmCancel( QSql::Op m );
+
+ virtual void handleError( const QSqlError& e );
+
+ virtual bool beginInsert();
+ virtual QWidget* beginUpdate ( int row, int col, bool replace );
+
+ bool eventFilter( QObject *o, QEvent *e );
+ void keyPressEvent( QKeyEvent* );
+ void resizeEvent ( QResizeEvent * );
+ void contentsMousePressEvent( QMouseEvent* e );
+ void contentsContextMenuEvent( QContextMenuEvent* e );
+ void endEdit( int row, int col, bool accept, bool replace );
+ QWidget * createEditor( int row, int col, bool initFromCell ) const;
+ void activateNextCell();
+ void reset();
+ void setSize( Q3SqlCursor* sql );
+ void repaintCell( int row, int col );
+ void paintCell ( QPainter * p, int row, int col, const QRect & cr,
+ bool selected, const QColorGroup &cg );
+ virtual void paintField( QPainter * p, const QSqlField* field, const QRect & cr,
+ bool selected );
+ void drawContents( QPainter * p, int cx, int cy, int cw, int ch );
+ virtual int fieldAlignment( const QSqlField* field );
+ void columnClicked ( int col );
+ void resizeData ( int len );
+
+ Q3TableItem * item ( int row, int col ) const;
+ void setItem ( int row, int col, Q3TableItem * item );
+ void clearCell ( int row, int col ) ;
+ void setPixmap ( int row, int col, const QPixmap & pix );
+ void takeItem ( Q3TableItem * i );
+
+private Q_SLOTS:
+ void loadNextPage();
+ void setCurrentSelection( int row, int col );
+ void updateCurrentSelection();
+ void sliderPressed();
+ void sliderReleased();
+ void doInsertCurrent();
+ void doUpdateCurrent();
+
+private:
+ void drawContents( QPainter *p);
+ QString fieldToString( const QSqlField * field );
+ void init();
+ QWidget* beginEdit ( int row, int col, bool replace );
+ void updateRow( int row );
+ void endInsert();
+ void endUpdate();
+ Q3DataTablePrivate* d;
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+ Q3DataTable( const Q3DataTable & );
+ Q3DataTable &operator=( const Q3DataTable & );
+#endif
+};
+
+#endif // QT_NO_SQL_VIEW_WIDGETS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DATATABLE_H
diff --git a/src/qt3support/sql/q3dataview.cpp b/src/qt3support/sql/q3dataview.cpp
new file mode 100644
index 0000000..c14ed7e
--- /dev/null
+++ b/src/qt3support/sql/q3dataview.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dataview.h"
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+#include "private/q3sqlmanager_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3DataViewPrivate
+{
+public:
+ Q3DataViewPrivate() {}
+ Q3SqlFormManager frm;
+};
+
+
+/*!
+ \class Q3DataView
+ \brief The Q3DataView class provides read-only SQL forms.
+
+ \compat
+
+ This class provides a form which displays SQL field data from a
+ record buffer. Because Q3DataView does not support editing it uses
+ less resources than a Q3DataBrowser. This class is well suited for
+ displaying read-only data from a SQL database.
+
+ If you want a to present your data in an editable form use
+ Q3DataBrowser; if you want a table-based presentation of your data
+ use Q3DataTable.
+
+ The form is associated with the data view with setForm() and the
+ record is associated with setRecord(). You can also pass a
+ QSqlRecord to the refresh() function which will set the record to
+ the given record and read the record's fields into the form.
+*/
+
+/*!
+ Constructs a data view which is a child of \a parent, called \a
+ name, and with widget flags \a fl.
+*/
+
+Q3DataView::Q3DataView(QWidget *parent, const char *name, Qt::WindowFlags fl)
+ : QWidget(parent, name, fl)
+{
+ d = new Q3DataViewPrivate();
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3DataView::~Q3DataView()
+{
+ delete d;
+}
+
+/*!
+ Clears the default form's values. If there is no default form,
+ nothing happens. All the values are set to their 'zero state',
+ e.g. 0 for numeric fields, "" for string fields.
+*/
+
+void Q3DataView::clearValues()
+{
+ d->frm.clearValues();
+}
+
+/*!
+ Sets the form used by the data view to \a form. If a record has
+ already been assigned to the data view, the form will display that
+ record's data.
+
+ \sa form()
+*/
+
+void Q3DataView::setForm(Q3SqlForm* form)
+{
+ d->frm.setForm(form);
+}
+
+
+/*!
+ Returns the default form used by the data view, or 0 if there is
+ none.
+
+ \sa setForm()
+*/
+
+Q3SqlForm* Q3DataView::form()
+{
+ return d->frm.form();
+}
+
+
+/*!
+ Sets the record used by the data view to \a record. If a form has
+ already been assigned to the data view, the form will display the
+ data from \a record in that form.
+
+ \sa record()
+*/
+
+void Q3DataView::setRecord(QSqlRecord* record)
+{
+ d->frm.setRecord(record);
+}
+
+
+/*!
+ Returns the default record used by the data view, or 0 if there is
+ none.
+
+ \sa setRecord()
+*/
+
+QSqlRecord* Q3DataView::record()
+{
+ return d->frm.record();
+}
+
+
+/*!
+ Causes the default form to read its fields from the record buffer.
+ If there is no default form, or no record, nothing happens.
+
+ \sa setForm()
+*/
+
+void Q3DataView::readFields()
+{
+ d->frm.readFields();
+}
+
+/*!
+ Causes the default form to write its fields to the record buffer.
+ If there is no default form, or no record, nothing happens.
+
+ \sa setForm()
+*/
+
+void Q3DataView::writeFields()
+{
+ d->frm.writeFields();
+}
+
+/*!
+ Causes the default form to display the contents of \a buf. If
+ there is no default form, nothing happens.The \a buf also becomes
+ the default record for all subsequent calls to readFields() and
+ writefields(). This slot is equivalant to calling:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3dataview.cpp 0
+
+ \sa setRecord() readFields()
+*/
+
+void Q3DataView::refresh(QSqlRecord* buf)
+{
+ if (buf && buf != record())
+ setRecord(buf);
+ readFields();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SQL_VIEW_WIDGETS
diff --git a/src/qt3support/sql/q3dataview.h b/src/qt3support/sql/q3dataview.h
new file mode 100644
index 0000000..287debe
--- /dev/null
+++ b/src/qt3support/sql/q3dataview.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DATAVIEW_H
+#define Q3DATAVIEW_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL_VIEW_WIDGETS
+
+class Q3SqlForm;
+class QSqlRecord;
+class Q3DataViewPrivate;
+
+class Q_COMPAT_EXPORT Q3DataView : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Q3DataView(QWidget* parent=0, const char* name=0, Qt::WindowFlags fl = 0);
+ ~Q3DataView();
+
+ virtual void setForm(Q3SqlForm* form);
+ Q3SqlForm* form();
+ virtual void setRecord(QSqlRecord* record);
+ QSqlRecord* record();
+
+public Q_SLOTS:
+ virtual void refresh(QSqlRecord* buf);
+ virtual void readFields();
+ virtual void writeFields();
+ virtual void clearValues();
+
+private:
+ Q_DISABLE_COPY(Q3DataView)
+
+ Q3DataViewPrivate* d;
+};
+
+#endif // QT_NO_SQL_VIEW_WIDGETS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DATAVIEW_H
diff --git a/src/qt3support/sql/q3editorfactory.cpp b/src/qt3support/sql/q3editorfactory.cpp
new file mode 100644
index 0000000..e55b488
--- /dev/null
+++ b/src/qt3support/sql/q3editorfactory.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3cleanuphandler.h"
+#include "qlabel.h"
+#include "qlineedit.h"
+#include "qspinbox.h"
+#include "qcombobox.h"
+
+#include "q3editorfactory.h"
+#include "qdatetimeedit.h"
+
+#ifndef QT_NO_SQL_EDIT_WIDGETS
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3EditorFactory
+ \brief The Q3EditorFactory class is used to create editor widgets
+ for QVariant data types.
+
+ \compat
+
+ Each editor factory provides the createEditor() function which
+ given a QVariant will create and return a QWidget that can edit
+ that QVariant. For example if you have a QVariant::String type, a
+ QLineEdit would be the default editor returned, whereas a
+ QVariant::Int's default editor would be a QSpinBox.
+
+ If you want to create different editors for fields with the same
+ data type, subclass Q3EditorFactory and reimplement the
+ createEditor() function.
+*/
+
+/*!
+ Constructs an editor factory with parent \a parent.
+*/
+
+Q3EditorFactory::Q3EditorFactory (QObject * parent)
+ : QObject(parent)
+{
+
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3EditorFactory::~Q3EditorFactory()
+{
+
+}
+
+static Q3EditorFactory * defaultfactory = 0;
+static Q3CleanupHandler< Q3EditorFactory > q_cleanup_editor_factory;
+
+/*!
+ Returns an instance of a default editor factory.
+*/
+
+Q3EditorFactory * Q3EditorFactory::defaultFactory()
+{
+ if(defaultfactory == 0){
+ defaultfactory = new Q3EditorFactory();
+ q_cleanup_editor_factory.add(&defaultfactory);
+ }
+
+ return defaultfactory;
+}
+
+/*!
+ Replaces the default editor factory with \a factory.
+ \e{Q3EditorFactory takes ownership of factory, and destroys it
+ when it is no longer needed.}
+*/
+
+void Q3EditorFactory::installDefaultFactory(Q3EditorFactory * factory)
+{
+ if(factory == 0 || factory == defaultfactory) return;
+
+ if(defaultfactory != 0){
+ q_cleanup_editor_factory.remove(&defaultfactory);
+ delete defaultfactory;
+ }
+ defaultfactory = factory;
+ q_cleanup_editor_factory.add(&defaultfactory);
+}
+
+/*!
+ Creates and returns the appropriate editor for the QVariant \a v.
+ If the QVariant is invalid, 0 is returned. The \a parent is passed
+ to the appropriate editor's constructor.
+*/
+
+QWidget * Q3EditorFactory::createEditor(QWidget * parent, const QVariant & v)
+{
+ QWidget * w = 0;
+ switch(v.type()){
+ case QVariant::Invalid:
+ w = 0;
+ break;
+ case QVariant::Bool:
+ w = new QComboBox(parent, "qt_editor_bool");
+ ((QComboBox *) w)->insertItem(QLatin1String("False"));
+ ((QComboBox *) w)->insertItem(QLatin1String("True"));
+ break;
+ case QVariant::UInt:
+ w = new QSpinBox(0, 999999, 1, parent, "qt_editor_spinbox");
+ break;
+ case QVariant::Int:
+ w = new QSpinBox(-999999, 999999, 1, parent, "qt_editor_int");
+ break;
+ case QVariant::String:
+ case QVariant::Double:
+ w = new QLineEdit(parent, "qt_editor_double");
+ ((QLineEdit*)w)->setFrame(false);
+ break;
+ case QVariant::Date: {
+ QDateTimeEdit *edit = new QDateTimeEdit(parent);
+ edit->setDisplayFormat(QLatin1String("yyyy/MM/dd"));
+ edit->setObjectName(QLatin1String("qt_editor_date"));
+ w = edit; }
+ break;
+ case QVariant::Time: {
+ QDateTimeEdit *edit = new QDateTimeEdit(parent);
+ edit->setDisplayFormat(QLatin1String("hh:mm"));
+ edit->setObjectName(QLatin1String("qt_editor_time"));
+ w = edit; }
+ break;
+ case QVariant::DateTime:
+ w = new QDateTimeEdit(parent);
+ w->setObjectName(QLatin1String("qt_editor_datetime"));
+ break;
+#ifndef QT_NO_LABEL
+ case QVariant::Pixmap:
+ w = new QLabel(parent, QLatin1String("qt_editor_pixmap"));
+ break;
+#endif
+ case QVariant::Palette:
+ case QVariant::Color:
+ case QVariant::Font:
+ case QVariant::Brush:
+ case QVariant::Bitmap:
+ case QVariant::Cursor:
+ case QVariant::Map:
+ case QVariant::StringList:
+ case QVariant::Rect:
+ case QVariant::Size:
+ case QVariant::IconSet:
+ case QVariant::Point:
+ case QVariant::PointArray:
+ case QVariant::Region:
+ case QVariant::SizePolicy:
+ case QVariant::ByteArray:
+ default:
+ w = new QWidget(parent, "qt_editor_default");
+ break;
+ }
+ return w;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SQL
diff --git a/src/qt3support/sql/q3editorfactory.h b/src/qt3support/sql/q3editorfactory.h
new file mode 100644
index 0000000..4fe8793
--- /dev/null
+++ b/src/qt3support/sql/q3editorfactory.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3EDITORFACTORY_H
+#define Q3EDITORFACTORY_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL_EDIT_WIDGETS
+
+class Q_COMPAT_EXPORT Q3EditorFactory : public QObject
+{
+public:
+ Q3EditorFactory (QObject * parent = 0);
+ ~Q3EditorFactory();
+
+ virtual QWidget * createEditor(QWidget * parent, const QVariant & v);
+
+ static Q3EditorFactory * defaultFactory();
+ static void installDefaultFactory(Q3EditorFactory * factory);
+
+private:
+ Q_DISABLE_COPY(Q3EditorFactory)
+};
+
+#endif // QT_NO_SQL_EDIT_WIDGETS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3EDITORFACTORY_H
diff --git a/src/qt3support/sql/q3sqlcursor.cpp b/src/qt3support/sql/q3sqlcursor.cpp
new file mode 100644
index 0000000..2624295
--- /dev/null
+++ b/src/qt3support/sql/q3sqlcursor.cpp
@@ -0,0 +1,1519 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include "q3sqlcursor.h"
+
+#ifndef QT_NO_SQL
+
+#include "qsqldriver.h"
+#include "qsqlresult.h"
+#include "qdatetime.h"
+#include "qsqldatabase.h"
+#include "qsql.h"
+#include "q3sqlrecordinfo.h"
+#include "q3sqlfieldinfo.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SqlCursorPrivate
+{
+public:
+
+ Q3SqlCursorPrivate(const QString& name, QSqlDatabase sdb)
+ : lastAt(QSql::BeforeFirst), nm(name), srt(name), md(0), db(sdb), q(0)
+ {}
+ ~Q3SqlCursorPrivate()
+ {
+ delete q;
+ }
+
+ QSqlQuery* query()
+ {
+ if (!q)
+ q = new QSqlQuery(QString(), db);
+ return q;
+ }
+
+ int lastAt;
+ QString nm; //name
+ QSqlIndex srt; //sort
+ QString ftr; //filter
+ int md; //mode
+ QSqlIndex priIndx; //primary index
+ QSqlRecord editBuffer;
+ // the primary index as it was before the user changed the values in editBuffer
+ QString editIndex;
+ Q3SqlRecordInfo infoBuffer;
+ QSqlDatabase db;
+ QSqlQuery *q;
+};
+
+QString qOrderByClause(const QSqlIndex & i, const QString& prefix = QString())
+{
+ QString str;
+ int k = i.count();
+ if(k == 0)
+ return QString();
+ str = QLatin1String(" order by ") + i.toString(prefix);
+ return str;
+}
+
+QString qWhereClause(const QString& prefix, QSqlField* field, const QSqlDriver* driver)
+{
+ QString f;
+ if (field && driver) {
+ if (!prefix.isEmpty())
+ f += prefix + QLatin1Char('.');
+ f += field->name();
+ if (field->isNull()) {
+ f += QLatin1String(" IS NULL");
+ } else {
+ f += QLatin1String(" = ") + driver->formatValue(field);
+ }
+ }
+ return f;
+}
+
+QString qWhereClause(QSqlRecord* rec, const QString& prefix, const QString& sep,
+ const QSqlDriver* driver)
+{
+ static QString blank(QLatin1Char(' '));
+ QString filter;
+ bool separator = false;
+ for (int j = 0; j < rec->count(); ++j) {
+ QSqlField f = rec->field(j);
+ if (rec->isGenerated(j)) {
+ if (separator)
+ filter += sep + blank;
+ filter += qWhereClause(prefix, &f, driver);
+ filter += blank;
+ separator = true;
+ }
+ }
+ return filter;
+}
+
+/*!
+ \class Q3SqlCursor
+ \brief The Q3SqlCursor class provides browsing and editing of SQL
+ tables and views.
+
+ \compat
+
+ A Q3SqlCursor is a database record (see \l QSqlRecord) that
+ corresponds to a table or view within an SQL database (see \l
+ QSqlDatabase). There are two buffers in a cursor, one used for
+ browsing and one used for editing records. Each buffer contains a
+ list of fields which correspond to the fields in the table or
+ view.
+
+ When positioned on a valid record, the browse buffer contains the
+ values of the current record's fields from the database. The edit
+ buffer is separate, and is used for editing existing records and
+ inserting new records.
+
+ For browsing data, a cursor must first select() data from the
+ database. After a successful select() the cursor is active
+ (isActive() returns true), but is initially not positioned on a
+ valid record (isValid() returns false). To position the cursor on
+ a valid record, use one of the navigation functions, next(),
+ previous(), first(), last(), or seek(). Once positioned on a valid
+ record, data can be retrieved from the browse buffer using
+ value(). If a navigation function is not successful, it returns
+ false, the cursor will no longer be positioned on a valid record
+ and the values returned by value() are undefined.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 0
+
+ In the above example, a cursor is created specifying a table or
+ view name in the database. Then, select() is called, which can be
+ optionally parameterised to filter and order the records
+ retrieved. Each record in the cursor is retrieved using next().
+ When next() returns false, there are no more records to process,
+ and the loop terminates.
+
+ For editing records (rows of data), a cursor contains a separate
+ edit buffer which is independent of the fields used when browsing.
+ The functions insert(), update() and del() operate on the edit
+ buffer. This allows the cursor to be repositioned to other
+ records while simultaneously maintaining a separate buffer for
+ edits. You can get a pointer to the edit buffer using
+ editBuffer(). The primeInsert(), primeUpdate() and primeDelete()
+ functions also return a pointer to the edit buffer and prepare it
+ for insert, update and delete respectively. Edit operations only
+ affect a single row at a time. Note that update() and del()
+ require that the table or view contain a primaryIndex() to ensure
+ that edit operations affect a unique record within the database.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 1
+
+ To edit an existing database record, first move to the record you
+ wish to update. Call primeUpdate() to get the pointer to the
+ cursor's edit buffer. Then use this pointer to modify the values
+ in the edit buffer. Finally, call update() to save the changes to
+ the database. The values in the edit buffer will be used to
+ locate the appropriate record when updating the database (see
+ primaryIndex()).
+
+ Similarly, when deleting an existing database record, first move
+ to the record you wish to delete. Then, call primeDelete() to get
+ the pointer to the edit buffer. Finally, call del() to delete the
+ record from the database. Again, the values in the edit buffer
+ will be used to locate and delete the appropriate record.
+
+ To insert a new record, call primeInsert() to get the pointer to
+ the edit buffer. Use this pointer to populate the edit buffer
+ with new values and then insert() the record into the database.
+
+ After calling insert(), update() or del(), the cursor is no longer
+ positioned on a valid record and can no longer be navigated
+ (isValid() return false). The reason for this is that any changes
+ made to the database will not be visible until select() is called
+ to refresh the cursor. You can change this behavior by passing
+ false to insert(), update() or del() which will prevent the cursor
+ from becoming invalid. The edits will still not be visible when
+ navigating the cursor until select() is called.
+
+ Q3SqlCursor contains virtual methods which allow editing behavior
+ to be customized by subclasses. This allows custom cursors to be
+ created that encapsulate the editing behavior of a database table
+ for an entire application. For example, a cursor can be customized
+ to always auto-number primary index fields, or provide fields with
+ suitable default values, when inserting new records. Q3SqlCursor
+ generates SQL statements which are sent to the database engine;
+ you can control which fields are included in these statements
+ using setGenerated().
+
+ Note that Q3SqlCursor does not inherit from QObject. This means
+ that you are responsible for destroying instances of this class
+ yourself. However if you create a Q3SqlCursor and use it in a
+ \l Q3DataTable, \l Q3DataBrowser or a \l Q3DataView these classes will
+ usually take ownership of the cursor and destroy it when they
+ don't need it anymore. The documentation for Q3DataTable,
+ Q3DataBrowser and Q3DataView explicitly states which calls take
+ ownership of the cursor.
+*/
+
+/*!
+ \enum Q3SqlCursor::Mode
+
+ This enum type describes how Q3SqlCursor operates on records in the
+ database.
+
+ \value ReadOnly the cursor can only SELECT records from the
+ database.
+
+ \value Insert the cursor can INSERT records into the database.
+
+ \value Update the cursor can UPDATE records in the database.
+
+ \value Delete the cursor can DELETE records from the database.
+
+ \value Writable the cursor can INSERT, UPDATE and DELETE records
+ in the database.
+*/
+
+/*!
+ \fn QVariant Q3SqlCursor::value(const QString &name) const
+
+ \overload
+
+ Returns the value of the field named \a name.
+*/
+
+/*!
+ \fn void Q3SqlCursor::setValue(const QString &name, const QVariant &val)
+
+ \overload
+
+ Sets the value for the field named \a name to \a val.
+*/
+
+/*!
+ Constructs a cursor on database \a db using table or view \a name.
+
+ If \a autopopulate is true (the default), the \a name of the
+ cursor must correspond to an existing table or view name in the
+ database so that field information can be automatically created.
+ If the table or view does not exist, the cursor will not be
+ functional.
+
+ The cursor is created with an initial mode of Q3SqlCursor::Writable
+ (meaning that records can be inserted, updated or deleted using
+ the cursor). If the cursor does not have a unique primary index,
+ update and deletes cannot be performed.
+
+ Note that \a autopopulate refers to populating the cursor with
+ meta-data, e.g. the names of the table's fields, not with
+ retrieving data. The select() function is used to populate the
+ cursor with data.
+
+ \sa setName() setMode()
+*/
+
+Q3SqlCursor::Q3SqlCursor(const QString & name, bool autopopulate, QSqlDatabase db)
+ : QSqlRecord(), QSqlQuery(QString(), db)
+{
+ d = new Q3SqlCursorPrivate(name, db);
+ setMode(Writable);
+ if (!d->nm.isEmpty())
+ setName(d->nm, autopopulate);
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+Q3SqlCursor::Q3SqlCursor(const Q3SqlCursor & other)
+ : QSqlRecord(other), QSqlQuery(other)
+{
+ d = new Q3SqlCursorPrivate(other.d->nm, other.d->db);
+ d->lastAt = other.d->lastAt;
+ d->nm = other.d->nm;
+ d->srt = other.d->srt;
+ d->ftr = other.d->ftr;
+ d->priIndx = other.d->priIndx;
+ d->editBuffer = other.d->editBuffer;
+ d->infoBuffer = other.d->infoBuffer;
+ d->q = 0; // do not share queries
+ setMode(other.mode());
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3SqlCursor::~Q3SqlCursor()
+{
+ delete d;
+}
+
+/*!
+ Sets the cursor equal to \a other.
+*/
+
+Q3SqlCursor& Q3SqlCursor::operator=(const Q3SqlCursor& other)
+{
+ QSqlRecord::operator=(other);
+ QSqlQuery::operator=(other);
+ delete d;
+ d = new Q3SqlCursorPrivate(other.d->nm, other.d->db);
+ d->lastAt = other.d->lastAt;
+ d->nm = other.d->nm;
+ d->srt = other.d->srt;
+ d->ftr = other.d->ftr;
+ d->priIndx = other.d->priIndx;
+ d->editBuffer = other.d->editBuffer;
+ d->infoBuffer = other.d->infoBuffer;
+ d->q = 0; // do not share queries
+ setMode(other.mode());
+ return *this;
+}
+
+/*!
+ Sets the current sort to \a sort. Note that no new records are
+ selected. To select new records, use select(). The \a sort will
+ apply to any subsequent select() calls that do not explicitly
+ specify a sort.
+*/
+
+void Q3SqlCursor::setSort(const QSqlIndex& sort)
+{
+ d->srt = sort;
+}
+
+/*!
+ Returns the current sort, or an empty index if there is no current
+ sort.
+*/
+QSqlIndex Q3SqlCursor::sort() const
+{
+ return d->srt;
+}
+
+/*!
+ Sets the current filter to \a filter. Note that no new records are
+ selected. To select new records, use select(). The \a filter will
+ apply to any subsequent select() calls that do not explicitly
+ specify a filter.
+
+ The filter is a SQL \c WHERE clause without the keyword 'WHERE',
+ e.g. \c{name='Dave'} which will be processed by the DBMS.
+*/
+void Q3SqlCursor::setFilter(const QString& filter)
+{
+ d->ftr = filter;
+}
+
+/*!
+ Returns the current filter, or an empty string if there is no
+ current filter.
+*/
+QString Q3SqlCursor::filter() const
+{
+ return d->ftr;
+}
+
+/*!
+ Sets the name of the cursor to \a name. If \a autopopulate is true
+ (the default), the \a name must correspond to a valid table or
+ view name in the database. Also, note that all references to the
+ cursor edit buffer become invalidated when fields are
+ auto-populated. See the Q3SqlCursor constructor documentation for
+ more information.
+*/
+void Q3SqlCursor::setName(const QString& name, bool autopopulate)
+{
+ d->nm = name;
+ if (autopopulate) {
+ if (driver()) {
+ d->infoBuffer = driver()->record(name);
+ *this = d->infoBuffer.toRecord();
+ d->editBuffer = *this;
+ d->priIndx = driver()->primaryIndex(name);
+ }
+ if (isEmpty())
+ qWarning("Q3SqlCursor::setName: unable to build record, does '%s' exist?", name.latin1());
+ }
+}
+
+/*!
+ Returns the name of the cursor.
+*/
+
+QString Q3SqlCursor::name() const
+{
+ return d->nm;
+}
+
+/*! \internal
+*/
+
+QString Q3SqlCursor::toString(const QString& prefix, const QString& sep) const
+{
+ QString pflist;
+ QString pfix = prefix.isEmpty() ? prefix : prefix + QLatin1Char('.');
+ bool comma = false;
+
+ for (int i = 0; i < count(); ++i) {
+ const QString fname = fieldName(i);
+ if (isGenerated(i)) {
+ if(comma)
+ pflist += sep + QLatin1Char(' ');
+ pflist += pfix + driver()->escapeIdentifier(fname, QSqlDriver::FieldName);
+ comma = true;
+ }
+ }
+ return pflist;
+}
+
+/*!
+ \internal
+
+ Assigns the record \a list.
+
+*/
+QSqlRecord & Q3SqlCursor::operator=(const QSqlRecord & list)
+{
+ return QSqlRecord::operator=(list);
+}
+
+/*!
+ Append a copy of field \a fieldInfo to the end of the cursor. Note
+ that all references to the cursor edit buffer become invalidated.
+*/
+
+void Q3SqlCursor::append(const Q3SqlFieldInfo& fieldInfo)
+{
+ d->editBuffer.append(fieldInfo.toField());
+ d->infoBuffer.append(fieldInfo);
+ QSqlRecord::append(fieldInfo.toField());
+}
+
+/*!
+ Removes all fields from the cursor. Note that all references to
+ the cursor edit buffer become invalidated.
+*/
+void Q3SqlCursor::clear()
+{
+ d->editBuffer.clear();
+ d->infoBuffer.clear();
+ QSqlRecord::clear();
+}
+
+
+/*!
+ Insert a copy of \a fieldInfo at position \a pos. If a field
+ already exists at \a pos, it is removed. Note that all references
+ to the cursor edit buffer become invalidated.
+*/
+
+void Q3SqlCursor::insert(int pos, const Q3SqlFieldInfo& fieldInfo)
+{
+ d->editBuffer.replace(pos, fieldInfo.toField());
+ d->infoBuffer[pos] = fieldInfo;
+ QSqlRecord::replace(pos, fieldInfo.toField());
+}
+
+/*!
+ Removes the field at \a pos. If \a pos does not exist, nothing
+ happens. Note that all references to the cursor edit buffer become
+ invalidated.
+*/
+
+void Q3SqlCursor::remove(int pos)
+{
+ d->editBuffer.remove(pos);
+ d->infoBuffer[pos] = Q3SqlFieldInfo();
+ QSqlRecord::remove(pos);
+}
+
+/*!
+ Sets the generated flag for the field \a name to \a generated. If
+ the field does not exist, nothing happens. Only fields that have
+ \a generated set to true are included in the SQL that is
+ generated by insert(), update() or del().
+*/
+
+void Q3SqlCursor::setGenerated(const QString& name, bool generated)
+{
+ int pos = indexOf(name);
+ if (pos == -1)
+ return;
+ QSqlRecord::setGenerated(name, generated);
+ d->editBuffer.setGenerated(name, generated);
+ d->infoBuffer[pos].setGenerated(generated);
+}
+
+/*!
+ \overload
+
+ Sets the generated flag for the field \a i to \a generated.
+*/
+void Q3SqlCursor::setGenerated(int i, bool generated)
+{
+ if (i < 0 || i >= (int)d->infoBuffer.count())
+ return;
+ QSqlRecord::setGenerated(i, generated);
+ d->editBuffer.setGenerated(i, generated);
+ d->infoBuffer[i].setGenerated(generated);
+}
+
+/*!
+ Returns the primary index associated with the cursor as defined in
+ the database, or an empty index if there is no primary index. If
+ \a setFromCursor is true (the default), the index fields are
+ populated with the corresponding values in the cursor's current
+ record.
+*/
+
+QSqlIndex Q3SqlCursor::primaryIndex(bool setFromCursor) const
+{
+ if (setFromCursor) {
+ for (int i = 0; i < d->priIndx.count(); ++i) {
+ const QString fn = d->priIndx.fieldName(i);
+ if (contains(fn))
+ d->priIndx.setValue(i, QSqlRecord::value(fn));
+ }
+ }
+ return d->priIndx;
+}
+
+/*!
+ Sets the primary index associated with the cursor to the index \a
+ idx. Note that this index must contain a field or set of fields
+ which identify a unique record within the underlying database
+ table or view so that update() and del() will execute as expected.
+
+ \sa update() del()
+*/
+
+void Q3SqlCursor::setPrimaryIndex(const QSqlIndex& idx)
+{
+ d->priIndx = idx;
+}
+
+
+/*!
+ Returns an index composed of \a fieldNames, all in ASCending
+ order. Note that all field names must exist in the cursor,
+ otherwise an empty index is returned.
+
+ \sa QSqlIndex
+*/
+
+QSqlIndex Q3SqlCursor::index(const QStringList& fieldNames) const
+{
+ QSqlIndex idx;
+ for (QStringList::ConstIterator it = fieldNames.begin(); it != fieldNames.end(); ++it) {
+ QSqlField f = field((*it));
+ if (!f.isValid()) { /* all fields must exist */
+ idx.clear();
+ break;
+ }
+ idx.append(f);
+ }
+ return idx;
+}
+
+/*!
+ \overload
+
+ Returns an index based on \a fieldName.
+*/
+
+QSqlIndex Q3SqlCursor::index(const QString& fieldName) const
+{
+ QStringList fl(fieldName);
+ return index(fl);
+}
+
+/*!
+ Selects all fields in the cursor from the database matching the
+ filter criteria \a filter. The data is returned in the order
+ specified by the index \a sort. Returns true if the data was
+ successfully selected; otherwise returns false.
+
+ The \a filter is a string containing a SQL \c WHERE clause but
+ without the 'WHERE' keyword. The cursor is initially positioned at
+ an invalid row after this function is called. To move to a valid
+ row, use seek(), first(), last(), previous() or next().
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 2
+
+ The filter will apply to any subsequent select() calls that do not
+ explicitly specify another filter. Similarly the sort will apply
+ to any subsequent select() calls that do not explicitly specify
+ another sort.
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 3
+
+*/
+
+bool Q3SqlCursor::select(const QString & filter, const QSqlIndex & sort)
+{
+ QString fieldList(toString(d->nm));
+ if (fieldList.isEmpty())
+ return false;
+ QString str(QLatin1String("select ") + fieldList);
+ str += QLatin1String(" from ") + d->nm;
+ if (!filter.isEmpty()) {
+ d->ftr = filter;
+ str += QLatin1String(" where ") + filter;
+ } else
+ d->ftr.clear();
+ if (sort.count() > 0)
+ str += QLatin1String(" order by ") + sort.toString(d->nm);
+ d->srt = sort;
+ return exec(str);
+}
+
+/*!
+ \overload
+
+ Selects all fields in the cursor from the database. The rows are
+ returned in the order specified by the last call to setSort() or
+ the last call to select() that specified a sort, whichever is the
+ most recent. If there is no current sort, the order in which the
+ rows are returned is undefined. The records are filtered according
+ to the filter specified by the last call to setFilter() or the
+ last call to select() that specified a filter, whichever is the
+ most recent. If there is no current filter, all records are
+ returned. The cursor is initially positioned at an invalid row. To
+ move to a valid row, use seek(), first(), last(), previous() or
+ next().
+
+ \sa setSort() setFilter()
+*/
+
+bool Q3SqlCursor::select()
+{
+ return select(filter(), sort());
+}
+
+/*!
+ \overload
+
+ Selects all fields in the cursor from the database. The data is
+ returned in the order specified by the index \a sort. The records
+ are filtered according to the filter specified by the last call to
+ setFilter() or the last call to select() that specified a filter,
+ whichever is the most recent. The cursor is initially positioned
+ at an invalid row. To move to a valid row, use seek(), first(),
+ last(), previous() or next().
+*/
+
+bool Q3SqlCursor::select(const QSqlIndex& sort)
+{
+ return select(filter(), sort);
+}
+
+/*!
+ \overload
+
+ Selects all fields in the cursor matching the filter index \a
+ filter. The data is returned in the order specified by the index
+ \a sort. The \a filter index works by constructing a WHERE clause
+ using the names of the fields from the \a filter and their values
+ from the current cursor record. The cursor is initially positioned
+ at an invalid row. To move to a valid row, use seek(), first(),
+ last(), previous() or next(). This function is useful, for example,
+ for retrieving data based upon a table's primary index:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 4
+
+ In this example the QSqlIndex, pk, is used for two different
+ purposes. When used as the filter (first) argument, the field
+ names it contains are used to construct the WHERE clause, each set
+ to the current cursor value, \c{WHERE id=10}, in this case. When
+ used as the sort (second) argument the field names it contains are
+ used for the ORDER BY clause, \c{ORDER BY id} in this example.
+*/
+
+bool Q3SqlCursor::select(const QSqlIndex & filter, const QSqlIndex & sort)
+{
+ return select(toString(filter, this, d->nm, QString(QLatin1Char('=')), QLatin1String("and")), sort);
+}
+
+/*!
+ Sets the cursor mode to \a mode. This value can be an OR'ed
+ combination of \l Q3SqlCursor::Mode values. The default mode for a
+ cursor is Q3SqlCursor::Writable.
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 5
+*/
+
+void Q3SqlCursor::setMode(int mode)
+{
+ d->md = mode;
+}
+
+/*!
+ Returns the current cursor mode.
+
+ \sa setMode()
+*/
+
+int Q3SqlCursor::mode() const
+{
+ return d->md;
+}
+
+/*!
+ Sets field \a name to \a calculated. If the field \a name does not
+ exist, nothing happens. The value of a calculated field is set by
+ the calculateField() virtual function which you must reimplement
+ (or the field value will be an invalid QVariant). Calculated
+ fields do not appear in generated SQL statements sent to the
+ database.
+
+ \sa calculateField()
+*/
+
+void Q3SqlCursor::setCalculated(const QString& name, bool calculated)
+{
+ int pos = indexOf(name);
+ if (pos < 0)
+ return;
+ d->infoBuffer[pos].setCalculated(calculated);
+ if (calculated)
+ setGenerated(pos, false);
+}
+
+/*!
+ Returns true if the field \a name exists and is calculated;
+ otherwise returns false.
+
+ \sa setCalculated()
+*/
+
+bool Q3SqlCursor::isCalculated(const QString& name) const
+{
+ int pos = indexOf(name);
+ if (pos < 0)
+ return false;
+ return d->infoBuffer[pos].isCalculated();
+}
+
+/*!
+ Sets field \a{name}'s trimmed status to \a trim. If the field \a
+ name does not exist, nothing happens.
+
+ When a trimmed field of type string is read from the
+ database any trailing (right-most) spaces are removed.
+
+ \sa isTrimmed() QVariant
+*/
+
+void Q3SqlCursor::setTrimmed(const QString& name, bool trim)
+{
+ int pos = indexOf(name);
+ if (pos < 0)
+ return;
+ d->infoBuffer[pos].setTrim(trim);
+}
+
+/*!
+ Returns true if the field \a name exists and is trimmed; otherwise
+ returns false.
+
+ When a trimmed field of type string or cstring is read from the
+ database any trailing (right-most) spaces are removed.
+
+ \sa setTrimmed()
+*/
+
+bool Q3SqlCursor::isTrimmed(const QString& name) const
+{
+ int pos = indexOf(name);
+ if (pos < 0)
+ return false;
+ return d->infoBuffer[pos].isTrim();
+}
+
+/*!
+ Returns true if the cursor is read-only; otherwise returns false.
+ The default is false. Read-only cursors cannot be edited using
+ insert(), update() or del().
+
+ \sa setMode()
+*/
+
+bool Q3SqlCursor::isReadOnly() const
+{
+ return d->md == 0;
+}
+
+/*!
+ Returns true if the cursor will perform inserts; otherwise returns
+ false.
+
+ \sa setMode()
+*/
+
+bool Q3SqlCursor::canInsert() const
+{
+ return ((d->md & Insert) == Insert) ;
+}
+
+
+/*!
+ Returns true if the cursor will perform updates; otherwise returns
+ false.
+
+ \sa setMode()
+*/
+
+bool Q3SqlCursor::canUpdate() const
+{
+ return ((d->md & Update) == Update) ;
+}
+
+/*!
+ Returns true if the cursor will perform deletes; otherwise returns
+ false.
+
+ \sa setMode()
+*/
+
+bool Q3SqlCursor::canDelete() const
+{
+ return ((d->md & Delete) == Delete) ;
+}
+
+/*!
+ \overload
+
+ Returns a formatted string composed of the \a prefix (e.g. table
+ or view name), ".", the \a field name, the \a fieldSep and the
+ field value. If the \a prefix is empty then the string will begin
+ with the \a field name. This function is useful for generating SQL
+ statements.
+*/
+
+QString Q3SqlCursor::toString(const QString& prefix, QSqlField* field, const QString& fieldSep) const
+{
+ QString f;
+ if (field && driver()) {
+ f = (prefix.length() > 0 ? prefix + QLatin1Char('.') : QString()) + driver()->escapeIdentifier(field->name(), QSqlDriver::FieldName);
+ f += QLatin1Char(' ') + fieldSep + QLatin1Char(' ');
+ if (field->isNull()) {
+ f += QLatin1String("NULL");
+ } else {
+ f += driver()->formatValue(field);
+ }
+ }
+ return f;
+}
+
+/*!
+ Returns a formatted string composed of all the fields in \a rec.
+ Each field is composed of the \a prefix (e.g. table or view name),
+ ".", the field name, the \a fieldSep and the field value. If the
+ \a prefix is empty then each field will begin with the field name.
+ The fields are then joined together separated by \a sep. Fields
+ where isGenerated() returns false are not included. This function
+ is useful for generating SQL statements.
+*/
+
+QString Q3SqlCursor::toString(QSqlRecord* rec, const QString& prefix, const QString& fieldSep,
+ const QString& sep) const
+{
+ static QString blank(QLatin1Char(' '));
+ QString filter;
+ bool separator = false;
+ for (int j = 0; j < count(); ++j) {
+ QSqlField f = rec->field(j);
+ if (rec->isGenerated(j)) {
+ if (separator)
+ filter += sep + blank;
+ filter += toString(prefix, &f, fieldSep);
+ filter += blank;
+ separator = true;
+ }
+ }
+ return filter;
+}
+
+/*!
+ \overload
+
+ Returns a formatted string composed of all the fields in the index
+ \a i. Each field is composed of the \a prefix (e.g. table or view
+ name), ".", the field name, the \a fieldSep and the field value.
+ If the \a prefix is empty then each field will begin with the field
+ name. The field values are taken from \a rec. The fields are then
+ joined together separated by \a sep. Fields where isGenerated()
+ returns false are ignored. This function is useful for generating
+ SQL statements.
+*/
+
+QString Q3SqlCursor::toString(const QSqlIndex& i, QSqlRecord* rec, const QString& prefix,
+ const QString& fieldSep, const QString& sep) const
+{
+ QString filter;
+ bool separator = false;
+ for(int j = 0; j < i.count(); ++j){
+ if (rec->isGenerated(j)) {
+ if(separator) {
+ filter += QLatin1Char(' ') + sep + QLatin1Char(' ') ;
+ }
+ QString fn = i.fieldName(j);
+ QSqlField f = rec->field(fn);
+ filter += toString(prefix, &f, fieldSep);
+ separator = true;
+ }
+ }
+ return filter;
+}
+
+/*!
+ Inserts the current contents of the cursor's edit record buffer
+ into the database, if the cursor allows inserts. Returns the
+ number of rows affected by the insert. For error information, use
+ lastError().
+
+ If \a invalidate is true (the default), the cursor will no longer
+ be positioned on a valid record and can no longer be navigated. A
+ new select() call must be made before navigating to a valid
+ record.
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 6
+
+ In the above example, a cursor is created on the 'prices' table
+ and a pointer to the insert buffer is acquired using primeInsert().
+ Each field's value is set to the desired value and then insert()
+ is called to insert the data into the database. Remember: all edit
+ operations (insert(), update() and delete()) operate on the
+ contents of the cursor edit buffer and not on the contents of the
+ cursor itself.
+
+ \sa setMode() lastError()
+*/
+
+int Q3SqlCursor::insert(bool invalidate)
+{
+ if ((d->md & Insert) != Insert || !driver())
+ return false;
+ int k = d->editBuffer.count();
+ if (k == 0)
+ return 0;
+
+ QString fList;
+ QString vList;
+ bool comma = false;
+ // use a prepared query if the driver supports it
+ if (driver()->hasFeature(QSqlDriver::PreparedQueries)) {
+ int cnt = 0;
+ bool oraStyle = driver()->hasFeature(QSqlDriver::NamedPlaceholders);
+ for(int j = 0; j < k; ++j) {
+ QSqlField f = d->editBuffer.field(j);
+ if (d->editBuffer.isGenerated(j)) {
+ if (comma) {
+ fList += QLatin1Char(',');
+ vList += QLatin1Char(',');
+ }
+ fList += driver()->escapeIdentifier(f.name(), QSqlDriver::FieldName);
+ vList += (oraStyle == true) ? QLatin1String(":f") + QString::number(cnt) : QString(QLatin1Char('?'));
+ cnt++;
+ comma = true;
+ }
+ }
+ if (!comma) {
+ return 0;
+ }
+ QString str;
+ str.append(QLatin1String("insert into ")).append(name())
+ .append(QLatin1String(" (")).append(fList)
+ .append(QLatin1String(") values (")).append(vList). append(QLatin1Char(')'));
+
+ return applyPrepared(str, invalidate);
+ } else {
+ for(int j = 0; j < k; ++j) {
+ QSqlField f = d->editBuffer.field(j);
+ if (d->editBuffer.isGenerated(j)) {
+ if (comma) {
+ fList += QLatin1Char(',');
+ vList += QLatin1Char(',');
+ }
+ fList += driver()->escapeIdentifier(f.name(), QSqlDriver::FieldName);
+ vList += driver()->formatValue(&f);
+ comma = true;
+ }
+ }
+
+ if (!comma) {
+ // no valid fields found
+ return 0;
+ }
+ QString str;
+ str.append(QLatin1String("insert into ")).append(name()).append(QLatin1String(" ("))
+ .append(fList).append(QLatin1String(") values (")).append(vList). append (QLatin1String(")"));
+ return apply(str, invalidate);
+ }
+}
+
+/*!
+ Returns the current internal edit buffer. If \a copy is true (the
+ default is false), the current cursor field values are first
+ copied into the edit buffer. The edit buffer is valid as long as
+ the cursor remains valid. The cursor retains ownership of the
+ returned pointer, so it must not be deleted or modified.
+
+ \sa primeInsert(), primeUpdate() primeDelete()
+*/
+
+QSqlRecord* Q3SqlCursor::editBuffer(bool copy)
+{
+ sync();
+ if (copy) {
+ for(int i = 0; i < d->editBuffer.count(); i++) {
+ if (QSqlRecord::isNull(i)) {
+ d->editBuffer.setNull(i);
+ } else {
+ d->editBuffer.setValue(i, value(i));
+ }
+ }
+ }
+ return &d->editBuffer;
+}
+
+/*!
+ This function primes the edit buffer's field values for update and
+ returns the edit buffer. The default implementation copies the
+ field values from the current cursor record into the edit buffer
+ (therefore, this function is equivalent to calling editBuffer(
+ true)). The cursor retains ownership of the returned pointer, so
+ it must not be deleted or modified.
+
+ \sa editBuffer() update()
+*/
+
+QSqlRecord* Q3SqlCursor::primeUpdate()
+{
+ // memorize the primary keys as they were before the user changed the values in editBuffer
+ QSqlRecord* buf = editBuffer(true);
+ QSqlIndex idx = primaryIndex(false);
+ if (!idx.isEmpty())
+ d->editIndex = toString(idx, buf, d->nm, QString(QLatin1Char('=')), QLatin1String("and"));
+ else
+ d->editIndex = qWhereClause(buf, d->nm, QLatin1String("and"), driver());
+ return buf;
+}
+
+/*!
+ This function primes the edit buffer's field values for delete and
+ returns the edit buffer. The default implementation copies the
+ field values from the current cursor record into the edit buffer
+ (therefore, this function is equivalent to calling editBuffer(
+ true)). The cursor retains ownership of the returned pointer, so
+ it must not be deleted or modified.
+
+ \sa editBuffer() del()
+*/
+
+QSqlRecord* Q3SqlCursor::primeDelete()
+{
+ return editBuffer(true);
+}
+
+/*!
+ This function primes the edit buffer's field values for insert and
+ returns the edit buffer. The default implementation clears all
+ field values in the edit buffer. The cursor retains ownership of
+ the returned pointer, so it must not be deleted or modified.
+
+ \sa editBuffer() insert()
+*/
+
+QSqlRecord* Q3SqlCursor::primeInsert()
+{
+ d->editBuffer.clearValues();
+ return &d->editBuffer;
+}
+
+
+/*!
+ Updates the database with the current contents of the edit buffer.
+ Returns the number of records which were updated.
+ For error information, use lastError().
+
+ Only records which meet the filter criteria specified by the
+ cursor's primary index are updated. If the cursor does not contain
+ a primary index, no update is performed and 0 is returned.
+
+ If \a invalidate is true (the default), the current cursor can no
+ longer be navigated. A new select() call must be made before you
+ can move to a valid record. For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 7
+
+ In the above example, a cursor is created on the 'prices' table
+ and is positioned on the record to be updated. Then a pointer to
+ the cursor's edit buffer is acquired using primeUpdate(). A new
+ value is calculated and placed into the edit buffer with the
+ setValue() call. Finally, an update() call is made on the cursor
+ which uses the tables's primary index to update the record in the
+ database with the contents of the cursor's edit buffer. Remember:
+ all edit operations (insert(), update() and delete()) operate on
+ the contents of the cursor edit buffer and not on the contents of
+ the cursor itself.
+
+ Note that if the primary index does not uniquely distinguish
+ records the database may be changed into an inconsistent state.
+
+ \sa setMode() lastError()
+*/
+
+int Q3SqlCursor::update(bool invalidate)
+{
+ if (d->editIndex.isEmpty())
+ return 0;
+ return update(d->editIndex, invalidate);
+}
+
+/*!
+ \overload
+
+ Updates the database with the current contents of the cursor edit
+ buffer using the specified \a filter. Returns the number of
+ records which were updated.
+ For error information, use lastError().
+
+ Only records which meet the filter criteria are updated, otherwise
+ all records in the table are updated.
+
+ If \a invalidate is true (the default), the cursor can no longer
+ be navigated. A new select() call must be made before you can move
+ to a valid record.
+
+ \sa primeUpdate() setMode() lastError()
+*/
+
+int Q3SqlCursor::update(const QString & filter, bool invalidate)
+{
+ if ((d->md & Update) != Update) {
+ return false;
+ }
+ int k = count();
+ if (k == 0) {
+ return 0;
+ }
+
+ // use a prepared query if the driver supports it
+ if (driver()->hasFeature(QSqlDriver::PreparedQueries)) {
+ QString fList;
+ bool comma = false;
+ int cnt = 0;
+ bool oraStyle = driver()->hasFeature(QSqlDriver::NamedPlaceholders);
+ for(int j = 0; j < k; ++j) {
+ QSqlField f = d->editBuffer.field(j);
+ if (d->editBuffer.isGenerated(j)) {
+ if (comma) {
+ fList += QLatin1Char(',');
+ }
+ fList += f.name() + QLatin1String(" = ") + (oraStyle == true ? QLatin1String(":f") + QString::number(cnt) : QString(QLatin1Char('?')));
+ cnt++;
+ comma = true;
+ }
+ }
+ if (!comma) {
+ return 0;
+ }
+ QString str(QLatin1String("update ") + name() + QLatin1String(" set ") + fList);
+ if (filter.length()) {
+ str+= QLatin1String(" where ") + filter;
+ }
+ return applyPrepared(str, invalidate);
+ } else {
+ QString str = QLatin1String("update ") + name();
+ str += QLatin1String(" set ") + toString(&d->editBuffer, QString(), QString(QLatin1Char('=')), QString(QLatin1Char(',')));
+ if (filter.length()) {
+ str+= QLatin1String(" where ") + filter;
+ }
+ return apply(str, invalidate);
+ }
+}
+
+/*!
+ Deletes a record from the database using the cursor's primary
+ index and the contents of the cursor edit buffer. Returns the
+ number of records which were deleted.
+ For error information, use lastError().
+
+ Only records which meet the filter criteria specified by the
+ cursor's primary index are deleted. If the cursor does not contain
+ a primary index, no delete is performed and 0 is returned. If \a
+ invalidate is true (the default), the current cursor can no longer
+ be navigated. A new select() call must be made before you can move
+ to a valid record. For example:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 8
+
+ In the above example, a cursor is created on the 'prices' table
+ and positioned to the record to be deleted. First primeDelete() is
+ called to populate the edit buffer with the current cursor values,
+ e.g. with an id of 999, and then del() is called to actually
+ delete the record from the database. Remember: all edit operations
+ (insert(), update() and delete()) operate on the contents of the
+ cursor edit buffer and not on the contents of the cursor itself.
+
+ \sa primeDelete() setMode() lastError()
+*/
+
+int Q3SqlCursor::del(bool invalidate)
+{
+ QSqlIndex idx = primaryIndex(false);
+ if (idx.isEmpty())
+ return del(qWhereClause(&d->editBuffer, d->nm, QLatin1String("and"), driver()), invalidate);
+ return del(toString(primaryIndex(), &d->editBuffer, d->nm, QString(QLatin1Char('=')), QLatin1String("and")), invalidate);
+}
+
+/*!
+ \overload
+
+ Deletes the current cursor record from the database using the
+ filter \a filter. Only records which meet the filter criteria are
+ deleted. Returns the number of records which were deleted. If \a
+ invalidate is true (the default), the current cursor can no longer
+ be navigated. A new select() call must be made before you can move
+ to a valid record. For error information, use lastError().
+
+ The \a filter is an SQL \c WHERE clause, e.g. \c{id=500}.
+
+ \sa setMode() lastError()
+*/
+
+int Q3SqlCursor::del(const QString & filter, bool invalidate)
+{
+ if ((d->md & Delete) != Delete)
+ return 0;
+ int k = count();
+ if(k == 0) return 0;
+ QString str = QLatin1String("delete from ") + name();
+ if (filter.length())
+ str+= QLatin1String(" where ") + filter;
+ return apply(str, invalidate);
+}
+
+/*
+ \internal
+*/
+
+int Q3SqlCursor::apply(const QString& q, bool invalidate)
+{
+ int ar = 0;
+ if (invalidate) {
+ if (exec(q))
+ ar = numRowsAffected();
+ } else if (driver()) {
+ QSqlQuery* sql = d->query();
+ if (sql && sql->exec(q))
+ ar = sql->numRowsAffected();
+ }
+ return ar;
+}
+
+/*
+ \internal
+*/
+
+int Q3SqlCursor::applyPrepared(const QString& q, bool invalidate)
+{
+ int ar = 0;
+ QSqlQuery* sql = 0;
+
+ if (invalidate) {
+ sql = (QSqlQuery*)this;
+ d->lastAt = QSql::BeforeFirst;
+ } else {
+ sql = d->query();
+ }
+ if (!sql)
+ return 0;
+
+ if (invalidate || sql->lastQuery() != q) {
+ if (!sql->prepare(q))
+ return 0;
+ }
+
+ int cnt = 0;
+ int fieldCount = (int)count();
+ for (int j = 0; j < fieldCount; ++j) {
+ const QSqlField f = d->editBuffer.field(j);
+ if (d->editBuffer.isGenerated(j)) {
+ if (f.type() == QVariant::ByteArray)
+ sql->bindValue(cnt, f.value(), QSql::In | QSql::Binary);
+ else
+ sql->bindValue(cnt, f.value());
+ cnt++;
+ }
+ }
+ if (sql->exec()) {
+ ar = sql->numRowsAffected();
+ }
+ return ar;
+}
+
+/*!
+ Executes the SQL query \a sql. Returns true of the cursor is
+ active, otherwise returns false.
+*/
+bool Q3SqlCursor::exec(const QString & sql)
+{
+ d->lastAt = QSql::BeforeFirst;
+ QSqlQuery::exec(sql);
+ return isActive();
+}
+
+/*!
+ Protected virtual function which is called whenever a field needs
+ to be calculated. If calculated fields are being used, derived
+ classes must reimplement this function and return the appropriate
+ value for field \a name. The default implementation returns an
+ invalid QVariant.
+
+ \sa setCalculated()
+*/
+
+QVariant Q3SqlCursor::calculateField(const QString&)
+{
+ return QVariant();
+}
+
+/*! \internal
+ Ensure fieldlist is synced with query.
+
+*/
+
+static QString qTrim(const QString& s)
+{
+ QString result = s;
+ int end = result.length() - 1;
+ while (end >= 0 && result[end].isSpace()) // skip white space from end
+ end--;
+ result.truncate(end + 1);
+ return result;
+}
+
+/*! \internal
+ */
+
+void Q3SqlCursor::sync()
+{
+ if (isActive() && isValid() && d->lastAt != at()) {
+ d->lastAt = at();
+ int i = 0;
+ int j = 0;
+ bool haveCalculatedFields = false;
+ for (; i < count(); ++i) {
+ if (!haveCalculatedFields && d->infoBuffer[i].isCalculated()) {
+ haveCalculatedFields = true;
+ }
+ if (QSqlRecord::isGenerated(i)) {
+ QVariant v = QSqlQuery::value(j);
+ if ((v.type() == QVariant::String) &&
+ d->infoBuffer[i].isTrim()) {
+ v = qTrim(v.toString());
+ }
+ QSqlRecord::setValue(i, v);
+ if (QSqlQuery::isNull(j))
+ QSqlRecord::field(i).clear();
+ j++;
+ }
+ }
+ if (haveCalculatedFields) {
+ for (i = 0; i < count(); ++i) {
+ if (d->infoBuffer[i].isCalculated())
+ QSqlRecord::setValue(i, calculateField(fieldName(i)));
+ }
+ }
+ }
+}
+
+/*!
+ Returns the value of field number \a i.
+*/
+
+QVariant Q3SqlCursor::value(int i) const
+{
+ const_cast<Q3SqlCursor *>(this)->sync();
+ return QSqlRecord::value(i);
+}
+
+/*! \internal
+ cursors should be filled with Q3SqlFieldInfos...
+*/
+void Q3SqlCursor::append(const QSqlField& field)
+{
+ append(Q3SqlFieldInfo(field));
+}
+
+/*!
+ Returns true if the field \a i is NULL or if there is no field at
+ position \a i; otherwise returns false.
+
+ This is the same as calling QSqlRecord::isNull(\a i)
+*/
+bool Q3SqlCursor::isNull(int i) const
+{
+ const_cast<Q3SqlCursor *>(this)->sync();
+ return QSqlRecord::isNull(i);
+}
+/*!
+ \overload
+
+ Returns true if the field called \a name is NULL or if there is no
+ field called \a name; otherwise returns false.
+
+ This is the same as calling QSqlRecord::isNull(\a name)
+*/
+bool Q3SqlCursor::isNull(const QString& name) const
+{
+ const_cast<Q3SqlCursor *>(this)->sync();
+ return QSqlRecord::isNull(name);
+}
+
+/*! \internal */
+void Q3SqlCursor::setValue(int i, const QVariant& val)
+{
+ sync();
+#ifdef QT_DEBUG
+ qDebug("Q3SqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete().");
+#endif
+ QSqlRecord::setValue(i, val);
+}
+
+/*! \internal */
+bool Q3SqlCursor::seek(int i, bool relative)
+{
+ bool res = QSqlQuery::seek(i, relative);
+ sync();
+ return res;
+}
+
+/*! \internal */
+bool Q3SqlCursor::next()
+{
+ bool res = QSqlQuery::next();
+ sync();
+ return res;
+}
+
+/*!
+ \fn Q3SqlCursor::previous()
+
+ \internal
+*/
+
+/*! \internal */
+bool Q3SqlCursor::prev()
+{
+ bool res = QSqlQuery::previous();
+ sync();
+ return res;
+}
+
+/*! \internal */
+bool Q3SqlCursor::first()
+{
+ bool res = QSqlQuery::first();
+ sync();
+ return res;
+}
+
+/*! \internal */
+bool Q3SqlCursor::last()
+{
+ bool res = QSqlQuery::last();
+ sync();
+ return res;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/sql/q3sqlcursor.h b/src/qt3support/sql/q3sqlcursor.h
new file mode 100644
index 0000000..85bb457
--- /dev/null
+++ b/src/qt3support/sql/q3sqlcursor.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLCURSOR_H
+#define Q3SQLCURSOR_H
+
+#include <QtCore/qvariant.h>
+#include <QtSql/qsqldatabase.h>
+#include <QtSql/qsqlrecord.h>
+#include <QtCore/qstringlist.h>
+#include <QtSql/qsqlquery.h>
+#include <QtSql/qsqlindex.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL
+
+class Q3SqlCursorPrivate;
+class Q3SqlFieldInfo;
+
+class Q_COMPAT_EXPORT Q3SqlCursor : public QSqlRecord, public QSqlQuery
+{
+public:
+ Q3SqlCursor(const QString & name = QString(), bool autopopulate = true,
+ QSqlDatabase db = QSqlDatabase());
+ Q3SqlCursor(const Q3SqlCursor & other);
+ Q3SqlCursor& operator=(const Q3SqlCursor& other);
+ virtual ~Q3SqlCursor();
+
+ enum Mode {
+ ReadOnly = 0,
+ Insert = 1,
+ Update = 2,
+ Delete = 4,
+ Writable = 7
+ };
+
+ QVariant value(int i) const;
+ inline QVariant value(const QString &name) const { return value(indexOf(name)); }
+ virtual void setValue(int i, const QVariant &val);
+ inline void setValue(const QString &name, const QVariant &val) { setValue(indexOf(name), val); }
+ virtual QSqlIndex primaryIndex(bool prime = true) const;
+ virtual QSqlIndex index(const QStringList& fieldNames) const;
+ QSqlIndex index(const QString& fieldName) const;
+ virtual void setPrimaryIndex(const QSqlIndex& idx);
+
+ virtual void append(const Q3SqlFieldInfo& fieldInfo);
+ virtual void insert(int pos, const Q3SqlFieldInfo &fieldInfo);
+ virtual void remove(int pos);
+ virtual void clear();
+ virtual void setGenerated(const QString& name, bool generated);
+ virtual void setGenerated(int i, bool generated);
+
+ virtual QSqlRecord* editBuffer(bool copy = false);
+ virtual QSqlRecord* primeInsert();
+ virtual QSqlRecord* primeUpdate();
+ virtual QSqlRecord* primeDelete();
+ virtual int insert(bool invalidate = true);
+ virtual int update(bool invalidate = true);
+ virtual int del(bool invalidate = true);
+
+ virtual void setMode(int flags);
+ int mode() const;
+ virtual void setCalculated(const QString& name, bool calculated);
+ bool isCalculated(const QString& name) const;
+ virtual void setTrimmed(const QString& name, bool trim);
+ bool isTrimmed(const QString& name) const;
+
+ bool isReadOnly() const;
+ bool canInsert() const;
+ bool canUpdate() const;
+ bool canDelete() const;
+
+ bool select();
+ bool select(const QSqlIndex& sort);
+ bool select(const QSqlIndex & filter, const QSqlIndex & sort);
+ virtual bool select(const QString & filter, const QSqlIndex & sort = QSqlIndex());
+
+ virtual void setSort(const QSqlIndex& sort);
+ QSqlIndex sort() const;
+ virtual void setFilter(const QString& filter);
+ QString filter() const;
+ virtual void setName(const QString& name, bool autopopulate = true);
+ QString name() const;
+ QString toString(const QString& prefix = QString(),
+ const QString& sep = QLatin1String(",")) const;
+ bool isNull(int i) const;
+ bool isNull(const QString& name) const;
+ virtual bool seek(int i, bool relative = false);
+ virtual bool next();
+ inline bool previous() { return prev(); }
+ virtual bool prev();
+ virtual bool first();
+ virtual bool last();
+
+protected:
+ virtual bool exec(const QString & sql);
+
+ virtual QVariant calculateField(const QString& name);
+ virtual int update(const QString & filter, bool invalidate = true);
+ virtual int del(const QString & filter, bool invalidate = true);
+
+ virtual QString toString(const QString& prefix, QSqlField* field, const QString& fieldSep) const;
+ virtual QString toString(QSqlRecord* rec, const QString& prefix, const QString& fieldSep,
+ const QString& sep) const;
+ virtual QString toString(const QSqlIndex& i, QSqlRecord* rec, const QString& prefix,
+ const QString& fieldSep, const QString& sep) const;
+
+private:
+ void sync();
+ int apply(const QString& q, bool invalidate);
+ int applyPrepared(const QString& q, bool invalidate);
+ QSqlRecord& operator=(const QSqlRecord & list);
+ void append(const QSqlField& field);
+
+ Q3SqlCursorPrivate* d;
+};
+
+#endif // QT_NO_SQL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SQLCURSOR_H
diff --git a/src/qt3support/sql/q3sqleditorfactory.cpp b/src/qt3support/sql/q3sqleditorfactory.cpp
new file mode 100644
index 0000000..5792539
--- /dev/null
+++ b/src/qt3support/sql/q3sqleditorfactory.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3sqleditorfactory.h"
+
+#ifndef QT_NO_SQL_EDIT_WIDGETS
+
+#include "qsqlfield.h"
+#include "q3cleanuphandler.h"
+#include "qlabel.h"
+#include "qlineedit.h"
+#include "qspinbox.h"
+#include "qcombobox.h"
+#include "qdatetimeedit.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3SqlEditorFactory
+ \brief The Q3SqlEditorFactory class is used to create the editors
+ used by Q3DataTable and Q3SqlForm.
+
+ \compat
+
+ Q3SqlEditorFactory is used by Q3DataTable and Q3SqlForm to
+ automatically create appropriate editors for a given QSqlField.
+ For example if the field is a QVariant::String a QLineEdit would
+ be the default editor, whereas a QVariant::Int's default editor
+ would be a QSpinBox.
+
+ If you want to create different editors for fields with the same
+ data type, subclass Q3SqlEditorFactory and reimplement the
+ createEditor() function.
+
+ \sa Q3DataTable, Q3SqlForm
+*/
+
+
+/*!
+ Constructs a SQL editor factory with parent \a parent.
+*/
+
+Q3SqlEditorFactory::Q3SqlEditorFactory (QObject * parent)
+ : Q3EditorFactory(parent)
+{
+
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3SqlEditorFactory::~Q3SqlEditorFactory()
+{
+
+}
+
+static Q3SqlEditorFactory * defaultfactory = 0;
+static Q3CleanupHandler< Q3SqlEditorFactory > qsql_cleanup_editor_factory;
+
+/*!
+ Returns an instance of a default editor factory.
+*/
+
+Q3SqlEditorFactory * Q3SqlEditorFactory::defaultFactory()
+{
+ if(defaultfactory == 0){
+ defaultfactory = new Q3SqlEditorFactory();
+ qsql_cleanup_editor_factory.add(&defaultfactory);
+ }
+
+ return defaultfactory;
+}
+
+/*!
+ Replaces the default editor factory with \a factory. All
+ Q3DataTable and Q3SqlForm instantiations will use this new factory
+ for creating field editors. \e{Q3SqlEditorFactory takes ownership
+ of \a factory, and destroys it when it is no longer needed.}
+*/
+
+void Q3SqlEditorFactory::installDefaultFactory(Q3SqlEditorFactory * factory)
+{
+ if(factory == 0) return;
+
+ if(defaultfactory != 0){
+ qsql_cleanup_editor_factory.remove(&defaultfactory);
+ delete defaultfactory;
+ }
+ defaultfactory = factory;
+ qsql_cleanup_editor_factory.add(&defaultfactory);
+}
+
+/*!
+ Creates and returns the appropriate editor widget for the QVariant
+ \a variant.
+
+ The widget that is returned has the parent \a parent (which may be
+ zero). If \a variant is invalid, 0 is returned.
+*/
+
+QWidget * Q3SqlEditorFactory::createEditor(QWidget * parent,
+ const QVariant & variant)
+{
+ return Q3EditorFactory::createEditor(parent, variant);
+}
+
+/*!
+ \overload
+
+ Creates and returns the appropriate editor for the QSqlField \a
+ field.
+*/
+
+QWidget * Q3SqlEditorFactory::createEditor(QWidget * parent,
+ const QSqlField * field)
+{
+ if (!field) {
+ return 0;
+ }
+
+ QWidget * w = 0;
+ switch(field->type()){
+ case QVariant::Invalid:
+ w = 0;
+ break;
+ case QVariant::Bool:
+ w = new QComboBox(parent, "qt_editor_bool");
+ ((QComboBox *) w)->insertItem(QLatin1String("False"));
+ ((QComboBox *) w)->insertItem(QLatin1String("True"));
+ break;
+ case QVariant::UInt:
+ w = new QSpinBox(0, 2147483647, 1, parent, "qt_editor_spinbox");
+ break;
+ case QVariant::Int:
+ w = new QSpinBox(-2147483647, 2147483647, 1, parent, "qt_editor_int");
+ break;
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::String:
+ case QVariant::Double:
+ w = new QLineEdit(parent, "qt_editor_double");
+ ((QLineEdit*)w)->setFrame(false);
+ break;
+ case QVariant::Date: {
+ QDateTimeEdit *edit = new QDateTimeEdit(parent);
+ edit->setDisplayFormat(QLatin1String("yyyy/MM/dd"));
+ edit->setObjectName(QLatin1String("qt_editor_date"));
+ w = edit; }
+ break;
+ case QVariant::Time: {
+ QDateTimeEdit *edit = new QDateTimeEdit(parent);
+ edit->setDisplayFormat(QLatin1String("hh:mm"));
+ edit->setObjectName(QLatin1String("qt_editor_time"));
+ w = edit; }
+ break;
+ case QVariant::DateTime:
+ w = new QDateTimeEdit(parent);
+ w->setObjectName(QLatin1String("qt_editor_datetime"));
+ break;
+#ifndef QT_NO_LABEL
+ case QVariant::Pixmap:
+ w = new QLabel(parent, "qt_editor_pixmap");
+ break;
+#endif
+ case QVariant::Palette:
+ case QVariant::Color:
+ case QVariant::Font:
+ case QVariant::Brush:
+ case QVariant::Bitmap:
+ case QVariant::Cursor:
+ case QVariant::Map:
+ case QVariant::StringList:
+ case QVariant::Rect:
+ case QVariant::Size:
+ case QVariant::IconSet:
+ case QVariant::Point:
+ case QVariant::PointArray:
+ case QVariant::Region:
+ case QVariant::SizePolicy:
+ case QVariant::ByteArray:
+ default:
+ w = new QWidget(parent, "qt_editor_default");
+ break;
+ }
+ return w;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SQL
diff --git a/src/qt3support/sql/q3sqleditorfactory.h b/src/qt3support/sql/q3sqleditorfactory.h
new file mode 100644
index 0000000..b8bb3b0
--- /dev/null
+++ b/src/qt3support/sql/q3sqleditorfactory.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLEDITORFACTORY_H
+#define Q3SQLEDITORFACTORY_H
+
+#include <Qt3Support/q3editorfactory.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL_EDIT_WIDGETS
+
+class QSqlField;
+
+class Q_COMPAT_EXPORT Q3SqlEditorFactory : public Q3EditorFactory
+{
+public:
+ Q3SqlEditorFactory (QObject * parent = 0);
+ ~Q3SqlEditorFactory();
+ virtual QWidget * createEditor(QWidget * parent, const QVariant & variant);
+ virtual QWidget * createEditor(QWidget * parent, const QSqlField * field);
+
+ static Q3SqlEditorFactory * defaultFactory();
+ static void installDefaultFactory(Q3SqlEditorFactory * factory);
+
+private:
+ Q_DISABLE_COPY(Q3SqlEditorFactory)
+};
+
+#endif // QT_NO_SQL_EDIT_WIDGETS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SQLEDITORFACTORY_H
diff --git a/src/qt3support/sql/q3sqlfieldinfo.h b/src/qt3support/sql/q3sqlfieldinfo.h
new file mode 100644
index 0000000..ada22f5
--- /dev/null
+++ b/src/qt3support/sql/q3sqlfieldinfo.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLFIELDINFO_H
+#define Q3SQLFIELDINFO_H
+
+#ifndef QT_NO_SQL
+
+#include <QtCore/qglobal.h>
+#include <QtSql/qsqlfield.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+/* Q3SqlFieldInfo Class
+ obsoleted, use QSqlField instead
+*/
+
+class Q_COMPAT_EXPORT Q3SqlFieldInfo
+{
+ // class is obsoleted, won't change anyways,
+ // so no d pointer
+ int req, len, prec, tID;
+ uint gen: 1;
+ uint trim: 1;
+ uint calc: 1;
+ QString nm;
+ QVariant::Type typ;
+ QVariant defValue;
+
+public:
+ Q3SqlFieldInfo(const QString& name = QString(),
+ QVariant::Type typ = QVariant::Invalid,
+ int required = -1,
+ int len = -1,
+ int prec = -1,
+ const QVariant& defValue = QVariant(),
+ int sqlType = 0,
+ bool generated = true,
+ bool trim = false,
+ bool calculated = false) :
+ req(required), len(len), prec(prec), tID(sqlType),
+ gen(generated), trim(trim), calc(calculated),
+ nm(name), typ(typ), defValue(defValue) {}
+
+ virtual ~Q3SqlFieldInfo() {}
+
+ Q3SqlFieldInfo(const QSqlField & other)
+ {
+ nm = other.name();
+ typ = other.type();
+ switch (other.requiredStatus()) {
+ case QSqlField::Unknown: req = -1; break;
+ case QSqlField::Required: req = 1; break;
+ case QSqlField::Optional: req = 0; break;
+ }
+ len = other.length();
+ prec = other.precision();
+ defValue = other.defaultValue();
+ tID = other.typeID();
+ gen = other.isGenerated();
+ calc = false;
+ trim = false;
+ }
+
+ bool operator==(const Q3SqlFieldInfo& f) const
+ {
+ return (nm == f.nm &&
+ typ == f.typ &&
+ req == f.req &&
+ len == f.len &&
+ prec == f.prec &&
+ defValue == f.defValue &&
+ tID == f.tID &&
+ gen == f.gen &&
+ trim == f.trim &&
+ calc == f.calc);
+ }
+
+ QSqlField toField() const
+ { QSqlField f(nm, typ);
+ f.setRequiredStatus(QSqlField::RequiredStatus(req));
+ f.setLength(len);
+ f.setPrecision(prec);
+ f.setDefaultValue(defValue);
+ f.setSqlType(tID);
+ f.setGenerated(gen);
+ return f;
+ }
+ int isRequired() const
+ { return req; }
+ QVariant::Type type() const
+ { return typ; }
+ int length() const
+ { return len; }
+ int precision() const
+ { return prec; }
+ QVariant defaultValue() const
+ { return defValue; }
+ QString name() const
+ { return nm; }
+ int typeID() const
+ { return tID; }
+ bool isGenerated() const
+ { return gen; }
+ bool isTrim() const
+ { return trim; }
+ bool isCalculated() const
+ { return calc; }
+
+ virtual void setTrim(bool trim)
+ { this->trim = trim; }
+ virtual void setGenerated(bool generated)
+ { gen = generated; }
+ virtual void setCalculated(bool calculated)
+ { calc = calculated; }
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SQL
+
+#endif // Q3SQLFIELDINFO_H
diff --git a/src/qt3support/sql/q3sqlfieldinfo.qdoc b/src/qt3support/sql/q3sqlfieldinfo.qdoc
new file mode 100644
index 0000000..5c7707b
--- /dev/null
+++ b/src/qt3support/sql/q3sqlfieldinfo.qdoc
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3SqlFieldInfo
+ \brief The Q3SqlFieldInfo class stores meta data associated with a SQL field.
+
+ \compat
+
+ Q3SqlFieldInfo objects only store meta data; field values are
+ stored in QSqlField objects.
+
+ All values must be set in the constructor, and may be retrieved
+ using isRequired(), type(), length(), precision(), defaultValue(),
+ name(), isGenerated() and typeID().
+
+ \sa Q3SqlRecordInfo
+*/
+
+/*!
+ \fn Q3SqlFieldInfo::Q3SqlFieldInfo(const QString& name,
+ QVariant::Type typ,
+ int required,
+ int len,
+ int prec,
+ const QVariant& defValue,
+ int typeID,
+ bool generated,
+ bool trim,
+ bool calculated)
+
+ Constructs a Q3SqlFieldInfo with the following parameters:
+ \table
+ \row \i \a name \i the name of the field.
+ \row \i \a typ \i the field's type in a QVariant.
+ \row \i \a required \i greater than 0 if the field is required, 0
+ if its value can be NULL and less than 0 if it cannot be
+ determined whether the field is required or not.
+ \row \i \a len \i the length of the field. Note that for
+ non-character types some databases return either the length in
+ bytes or the number of digits. -1 signifies that the length cannot
+ be determined.
+ \row \i \a prec \i the precision of the field, or -1 if the field
+ has no precision or it cannot be determined.
+ \row \i \a defValue \i the default value that is inserted into
+ the table if none is specified by the user. QVariant() if there is
+ no default value or it cannot be determined.
+ \row \i \a typeID \i the internal typeID of the database system
+ (only useful for low-level programming). 0 if unknown.
+ \row \i \a generated \i TRUE indicates that this field should be
+ included in auto-generated SQL statments, e.g. in Q3SqlCursor.
+ \row \i \a trim \i TRUE indicates that widgets should remove
+ trailing whitespace from character fields. This does not affect
+ the field value but only its representation inside widgets.
+ \row \i \a calculated \i TRUE indicates that the value of this
+ field is calculated. The value of calculated fields can by
+ modified by subclassing Q3SqlCursor and overriding
+ Q3SqlCursor::calculateField().
+ \endtable
+*/
+
+/*!
+ \fn Q3SqlFieldInfo::~Q3SqlFieldInfo()
+
+ Destroys the object and frees any allocated resources.
+*/
+
+/*!
+ \fn Q3SqlFieldInfo::Q3SqlFieldInfo(const QSqlField & other)
+
+ Creates a Q3SqlFieldInfo object with the type and the name of the
+ QSqlField \a other.
+*/
+
+/*!
+ \fn bool Q3SqlFieldInfo::operator==(const Q3SqlFieldInfo& other) const
+
+ Assigns \a other to this field info and returns a reference to it.
+*/
+
+/*!
+ \fn QSqlField Q3SqlFieldInfo::toField() const
+
+ Returns an empty QSqlField based on the information in this
+ Q3SqlFieldInfo.
+*/
+
+/*!
+ \fn int Q3SqlFieldInfo::isRequired() const
+
+ Returns a value greater than 0 if the field is required (NULL
+ values are not allowed), 0 if it isn't required (NULL values are
+ allowed) or less than 0 if it cannot be determined whether the
+ field is required or not.
+*/
+
+/*!
+ \fn QVariant::Type Q3SqlFieldInfo::type() const
+
+ Returns the field's type or QVariant::Invalid if the type is
+ unknown.
+*/
+
+/*!
+ \fn int Q3SqlFieldInfo::length() const
+
+ Returns the field's length. For fields storing text the return
+ value is the maximum number of characters the field can hold. For
+ non-character fields some database systems return the number of
+ bytes needed or the number of digits allowed. If the length cannot
+ be determined -1 is returned.
+*/
+
+/*!
+ \fn int Q3SqlFieldInfo::precision() const
+
+ Returns the field's precision or -1 if the field has no precision
+ or it cannot be determined.
+*/
+
+/*!
+ \fn QVariant Q3SqlFieldInfo::defaultValue() const
+
+ Returns the field's default value or an empty QVariant if the
+ field has no default value or the value couldn't be determined.
+ The default value is the value inserted in the database when it
+ is not explicitly specified by the user.
+*/
+
+/*!
+ \fn QString Q3SqlFieldInfo::name() const
+
+ Returns the name of the field in the SQL table.
+*/
+
+/*!
+ \fn int Q3SqlFieldInfo::typeID() const
+
+ Returns the internal type identifier as returned from the database
+ system. The return value is 0 if the type is unknown.
+*/
+
+/*!
+ \fn bool Q3SqlFieldInfo::isGenerated() const
+
+ Returns TRUE if the field should be included in auto-generated
+ SQL statments, e.g. in Q3SqlCursor; otherwise returns FALSE.
+
+ \sa setGenerated()
+*/
+
+/*!
+ \fn bool Q3SqlFieldInfo::isTrim() const
+
+ Returns TRUE if trailing whitespace should be removed from
+ character fields; otherwise returns FALSE.
+
+ \sa setTrim()
+*/
+
+/*!
+ \fn bool Q3SqlFieldInfo::isCalculated() const
+
+ Returns TRUE if the field is calculated; otherwise returns FALSE.
+
+ \sa setCalculated()
+*/
+
+/*!
+ \fn void Q3SqlFieldInfo::setTrim(bool trim)
+
+ If \a trim is TRUE widgets should remove trailing whitespace from
+ character fields. This does not affect the field value but only
+ its representation inside widgets.
+
+ \sa isTrim()
+*/
+
+/*!
+ \fn void Q3SqlFieldInfo::setGenerated(bool generated)
+
+ \a generated set to FALSE indicates that this field should not appear
+ in auto-generated SQL statements (for example in Q3SqlCursor).
+
+ \sa isGenerated()
+*/
+
+/*!
+ \fn void Q3SqlFieldInfo::setCalculated(bool calculated)
+
+ \a calculated set to TRUE indicates that this field is a calculated
+ field. The value of calculated fields can by modified by subclassing
+ Q3SqlCursor and overriding Q3SqlCursor::calculateField().
+
+ \sa isCalculated()
+*/
diff --git a/src/qt3support/sql/q3sqlform.cpp b/src/qt3support/sql/q3sqlform.cpp
new file mode 100644
index 0000000..164d539
--- /dev/null
+++ b/src/qt3support/sql/q3sqlform.cpp
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3sqlform.h"
+
+#ifndef QT_NO_SQL_FORM
+
+#include "qsqlfield.h"
+#include "q3sqlpropertymap.h"
+#include "qsqlrecord.h"
+#include "qstringlist.h"
+#include "qwidget.h"
+#include "qhash.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SqlFormPrivate
+{
+public:
+ Q3SqlFormPrivate() : propertyMap(0), buf(0), dirty(false) {}
+ ~Q3SqlFormPrivate() { if (propertyMap) delete propertyMap; }
+ QStringList fld;
+ QHash <QString, QWidget*> wgt;
+ QMap <QWidget*, QSqlField *> map;
+ Q3SqlPropertyMap * propertyMap;
+ QSqlRecord* buf;
+ bool dirty;
+};
+
+/*!
+ \class Q3SqlForm
+ \brief The Q3SqlForm class creates and manages data entry forms
+ tied to SQL databases.
+
+ \compat
+
+ Typical use of a Q3SqlForm consists of the following steps:
+ \list
+ \i Create the widgets you want to appear in the form.
+ \i Create a cursor and navigate to the record to be edited.
+ \i Create the Q3SqlForm.
+ \i Set the form's record buffer to the cursor's update buffer.
+ \i Insert each widget and the field it is to edit into the form.
+ \i Use readFields() to update the editor widgets with values from
+ the database's fields.
+ \i Display the form and let the user edit values etc.
+ \i Use writeFields() to update the database's field values with
+ the values in the editor widgets.
+ \endlist
+
+ Note that a Q3SqlForm does not access the database directly, but
+ most often via QSqlFields which are part of a Q3SqlCursor. A
+ Q3SqlCursor::insert(), Q3SqlCursor::update() or Q3SqlCursor::del()
+ call is needed to actually write values to the database.
+
+ Some sample code to initialize a form successfully:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlform.cpp 0
+
+ If you want to use custom editors for displaying and editing data
+ fields, you must install a custom Q3SqlPropertyMap. The form
+ uses this object to get or set the value of a widget.
+
+ \sa installPropertyMap(), Q3SqlPropertyMap
+*/
+
+
+/*!
+ Constructs a Q3SqlForm with parent \a parent.
+*/
+Q3SqlForm::Q3SqlForm(QObject * parent)
+ : QObject(parent)
+{
+ d = new Q3SqlFormPrivate();
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+Q3SqlForm::~Q3SqlForm()
+{
+ delete d;
+}
+
+/*!
+ Installs a custom Q3SqlPropertyMap. This is useful if you plan to
+ create your own custom editor widgets.
+
+ Q3SqlForm takes ownership of \a pmap, so \a pmap is deleted when
+ Q3SqlForm goes out of scope.
+
+ \sa Q3DataTable::installEditorFactory()
+*/
+void Q3SqlForm::installPropertyMap(Q3SqlPropertyMap * pmap)
+{
+ if(d->propertyMap)
+ delete d->propertyMap;
+ d->propertyMap = pmap;
+}
+
+/*!
+ Sets \a buf as the record buffer for the form. To force the
+ display of the data from \a buf, use readFields().
+
+ \sa readFields() writeFields()
+*/
+
+void Q3SqlForm::setRecord(QSqlRecord* buf)
+{
+ d->dirty = true;
+ d->buf = buf;
+}
+
+/*!
+ Inserts a \a widget, and the name of the \a field it is to be
+ mapped to, into the form. To actually associate inserted widgets
+ with an edit buffer, use setRecord().
+
+ \sa setRecord()
+*/
+
+void Q3SqlForm::insert(QWidget * widget, const QString& field)
+{
+ d->dirty = true;
+ d->wgt.insert(field, widget);
+ d->fld += field;
+}
+
+/*!
+ \overload
+
+ Removes \a field from the form.
+*/
+
+void Q3SqlForm::remove(const QString& field)
+{
+ d->dirty = true;
+ int i = d->fld.indexOf(field);
+ if (i >= 0)
+ d->fld.removeAt(i);
+ d->wgt.remove(field);
+}
+
+/*!
+ \overload
+
+ Inserts a \a widget, and the \a field it is to be mapped to, into
+ the form.
+*/
+
+void Q3SqlForm::insert(QWidget * widget, QSqlField * field)
+{
+ d->map[widget] = field;
+}
+
+/*!
+ Removes a \a widget, and hence the field it's mapped to, from the
+ form.
+*/
+
+void Q3SqlForm::remove(QWidget * widget)
+{
+ d->map.remove(widget);
+}
+
+/*!
+ Clears the values in all the widgets, and the fields they are
+ mapped to, in the form, and sets them to NULL.
+*/
+void Q3SqlForm::clearValues()
+{
+ QMap< QWidget *, QSqlField * >::Iterator it;
+ for(it = d->map.begin(); it != d->map.end(); ++it){
+ QSqlField* f = (*it);
+ if (f)
+ f->clear();
+ }
+ readFields();
+}
+
+/*!
+ Removes every widget, and the fields they're mapped to, from the form.
+*/
+void Q3SqlForm::clear()
+{
+ d->dirty = true;
+ d->fld.clear();
+ clearMap();
+}
+
+/*!
+ Returns the number of widgets in the form.
+*/
+int Q3SqlForm::count() const
+{
+ return d->map.size();
+}
+
+/*!
+ Returns the \a{i}-th widget in the form. Useful for traversing
+ the widgets in the form.
+*/
+QWidget * Q3SqlForm::widget(int i) const
+{
+ QMap< QWidget *, QSqlField * >::ConstIterator it;
+ int cnt = 0;
+
+ if(i > d->map.size())
+ return 0;
+ for(it = d->map.constBegin(); it != d->map.constEnd(); ++it){
+ if(cnt++ == i)
+ return it.key();
+ }
+ return 0;
+}
+
+/*!
+ Returns the widget that field \a field is mapped to.
+*/
+QWidget * Q3SqlForm::fieldToWidget(QSqlField * field) const
+{
+ QMap< QWidget *, QSqlField * >::ConstIterator it;
+ for(it = d->map.constBegin(); it != d->map.constEnd(); ++it){
+ if(*it == field)
+ return it.key();
+ }
+ return 0;
+}
+
+/*!
+ Returns the SQL field that widget \a widget is mapped to.
+*/
+QSqlField * Q3SqlForm::widgetToField(QWidget * widget) const
+{
+ return d->map.value(widget, 0);
+}
+
+/*!
+ Updates the widgets in the form with current values from the SQL
+ fields they are mapped to.
+*/
+void Q3SqlForm::readFields()
+{
+ sync();
+ QSqlField * f;
+ QMap< QWidget *, QSqlField * >::Iterator it;
+ Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ Q3SqlPropertyMap::defaultMap() : d->propertyMap;
+ for(it = d->map.begin() ; it != d->map.end(); ++it){
+ f = widgetToField(it.key());
+ if(!f)
+ continue;
+ pmap->setProperty(it.key(), f->value());
+ }
+}
+
+/*!
+ Updates the SQL fields with values from the widgets they are
+ mapped to. To actually update the database with the contents of
+ the record buffer, use Q3SqlCursor::insert(), Q3SqlCursor::update()
+ or Q3SqlCursor::del() as appropriate.
+*/
+void Q3SqlForm::writeFields()
+{
+ sync();
+ QSqlField * f;
+ QMap< QWidget *, QSqlField * >::Iterator it;
+ Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ Q3SqlPropertyMap::defaultMap() : d->propertyMap;
+
+ for(it = d->map.begin() ; it != d->map.end(); ++it){
+ f = widgetToField(it.key());
+ if(!f)
+ continue;
+ f->setValue(pmap->property(it.key()));
+ }
+}
+
+/*!
+ Updates the widget \a widget with the value from the SQL field it
+ is mapped to. Nothing happens if no SQL field is mapped to the \a
+ widget.
+*/
+void Q3SqlForm::readField(QWidget * widget)
+{
+ sync();
+ QSqlField * field = 0;
+ Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ Q3SqlPropertyMap::defaultMap() : d->propertyMap;
+ field = widgetToField(widget);
+ if(field)
+ pmap->setProperty(widget, field->value());
+}
+
+/*!
+ Updates the SQL field with the value from the \a widget it is
+ mapped to. Nothing happens if no SQL field is mapped to the \a
+ widget.
+*/
+void Q3SqlForm::writeField(QWidget * widget)
+{
+ sync();
+ QSqlField * field = 0;
+ Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ?
+ Q3SqlPropertyMap::defaultMap() : d->propertyMap;
+ field = widgetToField(widget);
+ if(field)
+ field->setValue(pmap->property(widget));
+}
+
+/*! \internal
+*/
+
+void Q3SqlForm::sync()
+{
+ if (d->dirty) {
+ clearMap();
+ if (d->buf) {
+ for (int i = 0; i < d->fld.count(); ++i) {
+ const QSqlField *field = d->buf->fieldPtr(d->fld.at(i));
+ insert(d->wgt.value(d->fld.at(i)), const_cast<QSqlField *>(field));
+ }
+ }
+ }
+ d->dirty = false;
+}
+
+/*! \internal
+
+ Clears the internal map of widget/field associations
+*/
+
+void Q3SqlForm::clearMap()
+{
+ d->map.clear();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SQL
diff --git a/src/qt3support/sql/q3sqlform.h b/src/qt3support/sql/q3sqlform.h
new file mode 100644
index 0000000..28991de
--- /dev/null
+++ b/src/qt3support/sql/q3sqlform.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLFORM_H
+#define Q3SQLFORM_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL_FORM
+
+class QSqlField;
+class QSqlRecord;
+class Q3SqlEditorFactory;
+class Q3SqlPropertyMap;
+class QWidget;
+class Q3SqlFormPrivate;
+
+class Q_COMPAT_EXPORT Q3SqlForm : public QObject
+{
+ Q_OBJECT
+public:
+ Q3SqlForm(QObject * parent = 0);
+ ~Q3SqlForm();
+
+ virtual void insert(QWidget * widget, const QString& field);
+ virtual void remove(const QString& field);
+ int count() const;
+
+ QWidget * widget(int i) const;
+ QSqlField * widgetToField(QWidget * widget) const;
+ QWidget * fieldToWidget(QSqlField * field) const;
+
+ void installPropertyMap(Q3SqlPropertyMap * map);
+
+ virtual void setRecord(QSqlRecord* buf);
+
+public Q_SLOTS:
+ virtual void readField(QWidget * widget);
+ virtual void writeField(QWidget * widget);
+ virtual void readFields();
+ virtual void writeFields();
+
+ virtual void clear();
+ virtual void clearValues();
+
+protected:
+ virtual void insert(QWidget * widget, QSqlField * field);
+ virtual void remove(QWidget * widget);
+ void clearMap();
+
+private:
+ Q_DISABLE_COPY(Q3SqlForm)
+
+ virtual void sync();
+ Q3SqlFormPrivate* d;
+};
+
+#endif // QT_NO_SQL_FORM
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SQLFORM_H
diff --git a/src/qt3support/sql/q3sqlmanager_p.cpp b/src/qt3support/sql/q3sqlmanager_p.cpp
new file mode 100644
index 0000000..7c5f82c
--- /dev/null
+++ b/src/qt3support/sql/q3sqlmanager_p.cpp
@@ -0,0 +1,961 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3sqlmanager_p.h"
+
+#ifndef QT_NO_SQL
+
+#include "qapplication.h"
+#include "qcursor.h"
+#include "qwidget.h"
+#include "q3sqlcursor.h"
+#include "qsqlfield.h"
+#include "q3sqlform.h"
+#include "qsqldriver.h"
+#include "qstring.h"
+#include "qmessagebox.h"
+#include "qbitarray.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define QT_DEBUG_DATAMANAGER
+
+class Q3SqlCursorManagerPrivate
+{
+public:
+ Q3SqlCursorManagerPrivate()
+ : cur(0), autoDelete(false)
+ {}
+
+ QString ftr;
+ QStringList srt;
+ Q3SqlCursor* cur;
+ bool autoDelete;
+};
+
+static QSqlIndex indexFromStringList(const QStringList& l, const Q3SqlCursor* cursor)
+{
+ QSqlIndex newSort;
+ for (int i = 0; i < l.count(); ++i) {
+ QString f = l[i];
+ bool desc = false;
+ if (f.mid(f.length()-3) == QLatin1String("ASC"))
+ f = f.mid(0, f.length()-3);
+ if (f.mid(f.length()-4) == QLatin1String("DESC")) {
+ desc = true;
+ f = f.mid(0, f.length()-4);
+ }
+ int dot = f.lastIndexOf(QLatin1Char('.'));
+ if (dot != -1)
+ f = f.mid(dot+1);
+ const QSqlField field = cursor->field(f.trimmed());
+ if (field.isValid())
+ newSort.append(field, desc);
+ else
+ qWarning("QSqlIndex::indexFromStringList: unknown field: '%s'", f.latin1());
+ }
+ return newSort;
+}
+
+
+/*!
+ \class Q3SqlCursorManager
+ \brief The Q3SqlCursorManager class manages a database cursor.
+
+ \compat
+ \internal
+
+ This class provides common cursor management functionality. This
+ includes saving and applying sorts and filters, refreshing (i.e.,
+ re-selecting) the cursor and searching for records within the
+ cursor.
+
+*/
+
+/*! \internal
+
+ Constructs a cursor manager.
+
+*/
+
+Q3SqlCursorManager::Q3SqlCursorManager()
+{
+ d = new Q3SqlCursorManagerPrivate;
+}
+
+
+/*! \internal
+
+ Destroys the object and frees any allocated resources.
+
+*/
+
+Q3SqlCursorManager::~Q3SqlCursorManager()
+{
+ if (d->autoDelete)
+ delete d->cur;
+ delete d;
+}
+
+/*! \internal
+
+ Sets the manager's sort to the index \a sort. To apply the new
+ sort, use refresh().
+
+ */
+
+void Q3SqlCursorManager::setSort(const QSqlIndex& sort)
+{
+ setSort(sort.toStringList());
+}
+
+/*! \internal
+
+ Sets the manager's sort to the stringlist \a sort. To apply the
+ new sort, use refresh().
+
+ */
+
+void Q3SqlCursorManager::setSort(const QStringList& sort)
+{
+ d->srt = sort;
+}
+
+/*! \internal
+
+ Returns the current sort, or an empty stringlist if there is none.
+
+*/
+
+QStringList Q3SqlCursorManager::sort() const
+{
+ return d->srt;
+}
+
+/*! \internal
+
+ Sets the manager's filter to the string \a filter. To apply the
+ new filter, use refresh().
+
+*/
+
+void Q3SqlCursorManager::setFilter(const QString& filter)
+{
+ d->ftr = filter;
+}
+
+/*! \internal
+
+ Returns the current filter, or an empty string if there is none.
+
+*/
+
+QString Q3SqlCursorManager::filter() const
+{
+ return d->ftr;
+}
+
+/*! \internal
+
+ Sets auto-delete to \a enable. If true, the default cursor will
+ be deleted when necessary.
+
+ \sa autoDelete()
+*/
+
+void Q3SqlCursorManager::setAutoDelete(bool enable)
+{
+ d->autoDelete = enable;
+}
+
+
+/*! \internal
+
+ Returns true if auto-deletion is enabled, otherwise false.
+
+ \sa setAutoDelete()
+
+*/
+
+bool Q3SqlCursorManager::autoDelete() const
+{
+ return d->autoDelete;
+}
+
+/*! \internal
+
+ Sets the default cursor used by the manager to \a cursor. If \a
+ autoDelete is true (the default is false), the manager takes
+ ownership of the \a cursor pointer, which will be deleted when the
+ manager is destroyed, or when setCursor() is called again. To
+ activate the \a cursor use refresh().
+
+ \sa cursor()
+
+*/
+
+void Q3SqlCursorManager::setCursor(Q3SqlCursor* cursor, bool autoDelete)
+{
+ if (d->autoDelete)
+ delete d->cur;
+ d->cur = cursor;
+ d->autoDelete = autoDelete;
+}
+
+/*! \internal
+
+ Returns a pointer to the default cursor used for navigation, or 0
+ if there is no default cursor.
+
+ \sa setCursor()
+
+*/
+
+Q3SqlCursor* Q3SqlCursorManager::cursor() const
+{
+ return d->cur;
+}
+
+
+/*! \internal
+
+ Refreshes the manager using the default cursor. The manager's
+ filter and sort are applied. Returns true on success, false if an
+ error occurred or there is no current cursor.
+
+ \sa setFilter() setSort()
+
+*/
+
+bool Q3SqlCursorManager::refresh()
+{
+ Q3SqlCursor* cur = cursor();
+ if (!cur)
+ return false;
+ QString currentFilter = d->ftr;
+ QStringList currentSort = d->srt;
+ QSqlIndex newSort = indexFromStringList(currentSort, cur);
+ return cur->select(currentFilter, newSort);
+}
+
+/* \internal
+
+ Returns true if the \a buf field values that correspond to \a idx
+ match the field values in \a cur that correspond to \a idx.
+*/
+
+static bool index_matches(const Q3SqlCursor* cur, const QSqlRecord* buf,
+ const QSqlIndex& idx)
+{
+ bool indexEquals = false;
+ for (int i = 0; i < idx.count(); ++i) {
+ const QString fn(idx.field(i).name());
+ if (cur->value(fn) == buf->value(fn))
+ indexEquals = true;
+ else {
+ indexEquals = false;
+ break;
+ }
+ }
+ return indexEquals;
+}
+
+/*
+ Return less than, equal to or greater than 0 if buf1 is less than,
+ equal to or greater than buf2 according to fields described in idx.
+ (### Currently only uses first field.)
+*/
+
+static int compare_recs(const QSqlRecord* buf1, const QSqlRecord* buf2,
+ const QSqlIndex& idx)
+{
+ int cmp = 0;
+
+ int i = 0;
+ const QString fn(idx.field(i).name());
+ const QSqlField f1 = buf1->field(fn);
+
+ if (f1.isValid()) {
+ switch (f1.type()) { // ### more types?
+ case QVariant::String:
+ cmp = f1.value().toString().trimmed().compare(
+ buf2->value(fn).toString().trimmed());
+ break;
+ default:
+ if (f1.value().toDouble() < buf2->value(fn).toDouble())
+ cmp = -1;
+ else if (f1.value().toDouble() > buf2->value(fn).toDouble())
+ cmp = 1;
+ }
+ }
+
+ if (idx.isDescending(i))
+ cmp = -cmp;
+ return cmp;
+}
+
+#ifdef QT_DEBUG_DATAMANAGER
+static void debug_datamanager_buffer(const QString& msg, QSqlRecord* cursor)
+{
+ qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ qDebug("%s", msg.latin1());
+ for (int j = 0; j < cursor->count(); ++j) {
+ qDebug("%s", (cursor->field(j)->name() + " type:"
+ + QString(cursor->field(j)->value().typeName())
+ + " value:" + cursor->field(j)->value().toString())
+ .latin1());
+ }
+}
+#endif
+
+
+/*! \internal
+
+ Relocates the default cursor to the record matching the cursor's
+edit buffer. Only the field names specified by \a idx are used to
+determine an exact match of the cursor to the edit buffer. However,
+other fields in the edit buffer are also used during the search,
+therefore all fields in the edit buffer should be primed with desired
+values for the record being sought. This function is typically used
+to relocate a cursor to the correct position after an insert or
+update. For example:
+
+\snippet doc/src/snippets/code/src_qt3support_sql_q3sqlmanager_p.cpp 0
+
+*/
+
+//## possibly add sizeHint parameter
+bool Q3SqlCursorManager::findBuffer(const QSqlIndex& idx, int atHint)
+{
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug("Q3SqlCursorManager::findBuffer:");
+#endif
+ Q3SqlCursor* cur = cursor();
+ if (!cur)
+ return false;
+ if (!cur->isActive())
+ return false;
+ if (!idx.count()) {
+ if (cur->at() == QSql::BeforeFirst)
+ cur->next();
+ return false;
+ }
+ QSqlRecord* buf = cur->editBuffer();
+ bool indexEquals = false;
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug(" Checking hint...");
+#endif
+ /* check the hint */
+ if (cur->seek(atHint))
+ indexEquals = index_matches(cur, buf, idx);
+
+ if (!indexEquals) {
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug(" Checking current page...");
+#endif
+ /* check current page */
+ int pageSize = 20;
+ int startIdx = qMax(atHint - pageSize, 0);
+ int endIdx = atHint + pageSize;
+ for (int j = startIdx; j <= endIdx; ++j) {
+ if (cur->seek(j)) {
+ indexEquals = index_matches(cur, buf, idx);
+ if (indexEquals)
+ break;
+ }
+ }
+ }
+
+ if (!indexEquals && cur->driver()->hasFeature(QSqlDriver::QuerySize)
+ && cur->sort().count()) {
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug(" Using binary search...");
+#endif
+ // binary search based on record buffer and current sort fields
+ int lo = 0;
+ int hi = cur->size();
+ int mid;
+ if (compare_recs(buf, cur, cur->sort()) >= 0)
+ lo = cur->at();
+ while (lo != hi) {
+ mid = lo + (hi - lo) / 2;
+ if (!cur->seek(mid))
+ break;
+ if (index_matches(cur, buf, idx)) {
+ indexEquals = true;
+ break;
+ }
+ int c = compare_recs(buf, cur, cur->sort());
+ if (c < 0) {
+ hi = mid;
+ } else if (c == 0) {
+ // found it, but there may be duplicates
+ int at = mid;
+ do {
+ mid--;
+ if (!cur->seek(mid))
+ break;
+ if (index_matches(cur, buf, idx)) {
+ indexEquals = true;
+ break;
+ }
+ } while (compare_recs(buf, cur, cur->sort()) == 0);
+
+ if (!indexEquals) {
+ mid = at;
+ do {
+ mid++;
+ if (!cur->seek(mid))
+ break;
+ if (index_matches(cur, buf, idx)) {
+ indexEquals = true;
+ break;
+ }
+ } while (compare_recs(buf, cur, cur->sort()) == 0);
+ }
+ break;
+ } else if (c > 0) {
+ lo = mid + 1;
+ }
+ }
+ }
+
+ if (!indexEquals) {
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug(" Using brute search...");
+#endif
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+#endif
+ /* give up, use brute force */
+ int startIdx = 0;
+ if (cur->at() != startIdx) {
+ cur->seek(startIdx);
+ }
+ for (;;) {
+ indexEquals = false;
+ indexEquals = index_matches(cur, buf, idx);
+ if (indexEquals)
+ break;
+ if (!cur->next())
+ break;
+ }
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ }
+#ifdef QT_DEBUG_DATAMANAGER
+ qDebug(" Done, result:" + QString::number(indexEquals));
+#endif
+ return indexEquals;
+}
+
+#ifndef QT_NO_SQL_FORM
+
+class Q3SqlFormManagerPrivate
+{
+public:
+ Q3SqlFormManagerPrivate() : frm(0), rcd(0) {}
+ Q3SqlForm* frm;
+ QSqlRecord* rcd;
+};
+
+
+/*! \internal
+
+ Creates a form manager.
+
+*/
+
+Q3SqlFormManager::Q3SqlFormManager()
+{
+ d = new Q3SqlFormManagerPrivate();
+}
+
+/*! \internal
+
+ Destroys the object and frees any allocated resources.
+
+*/
+
+Q3SqlFormManager::~Q3SqlFormManager()
+{
+ delete d;
+}
+
+/*! \internal
+
+ Clears the default form values. If there is no default form,
+ nothing happens,
+
+*/
+
+void Q3SqlFormManager::clearValues()
+{
+ if (form())
+ form()->clearValues();
+}
+
+/*! \internal
+
+ Sets the form used by the form manager to \a form. If a record has
+ already been assigned to the form manager, that record is also used by
+ the \a form to display data.
+
+ \sa form()
+
+*/
+
+void Q3SqlFormManager::setForm(Q3SqlForm* form)
+{
+ d->frm = form;
+ if (d->rcd && d->frm)
+ d->frm->setRecord(d->rcd);
+}
+
+
+/*! \internal
+
+ Returns the default form used by the form manager, or 0 if there is
+ none.
+
+ \sa setForm()
+
+*/
+
+Q3SqlForm* Q3SqlFormManager::form()
+{
+ return d->frm;
+}
+
+
+/*! \internal
+
+ Sets the record used by the form manager to \a record. If a form has
+ already been assigned to the form manager, \a record is also used by
+ the default form to display data.
+
+ \sa record()
+
+*/
+
+void Q3SqlFormManager::setRecord(QSqlRecord* record)
+{
+ d->rcd = record;
+ if (d->frm) {
+ d->frm->setRecord(d->rcd);
+ }
+}
+
+
+/*! \internal
+
+ Returns the default record used by the form manager, or 0 if there is
+ none.
+
+ \sa setRecord()
+*/
+
+QSqlRecord* Q3SqlFormManager::record()
+{
+ return d->rcd;
+}
+
+
+/*! \internal
+
+ Causes the default form to read its fields . If there is no
+ default form, nothing happens.
+
+ \sa setForm()
+
+*/
+
+void Q3SqlFormManager::readFields()
+{
+ if (d->frm) {
+ d->frm->readFields();
+ }
+}
+
+/*! \internal
+
+ Causes the default form to write its fields . If there is no
+ default form, nothing happens.
+
+ \sa setForm()
+
+*/
+
+void Q3SqlFormManager::writeFields()
+{
+ if (d->frm) {
+ d->frm->writeFields();
+ }
+}
+
+#endif // QT_NO_SQL_FORM
+
+class Q3DataManagerPrivate
+{
+public:
+ Q3DataManagerPrivate()
+ : mode(QSql::None), autoEd(true), confEdits(3),
+ confCancs(false) {}
+ QSql::Op mode;
+ bool autoEd;
+ QBitArray confEdits;
+ bool confCancs;
+
+};
+
+/*!
+ \class Q3DataManager
+
+ \brief The Q3DataManager class is an internal class for implementing
+ the data-aware widgets.
+
+ \internal
+ \compat
+
+ Q3DataManager is a strictly internal class that acts as a base class
+ for other data-aware widgets.
+
+*/
+
+
+/*! \internal
+
+ Constructs an empty data handler.
+
+*/
+
+Q3DataManager::Q3DataManager()
+{
+ d = new Q3DataManagerPrivate();
+}
+
+
+/*! \internal
+
+ Destroys the object and frees any allocated resources.
+
+*/
+
+Q3DataManager::~Q3DataManager()
+{
+ delete d;
+}
+
+
+/*! \internal
+
+ Virtual function which is called when an error has occurred The
+ default implementation displays a warning message to the user with
+ information about the error.
+
+*/
+void Q3DataManager::handleError(QWidget* parent, const QSqlError& e)
+{
+#ifndef QT_NO_MESSAGEBOX
+ if (e.driverText().isEmpty() && e.databaseText().isEmpty()) {
+ QMessageBox::warning (parent, QLatin1String("Warning"), QLatin1String("An error occurred while accessing the database"));
+ } else {
+ QMessageBox::warning (parent, QLatin1String("Warning"), e.driverText() + QLatin1Char('\n') + e.databaseText(),
+ 0, 0);
+ }
+#endif // QT_NO_MESSAGEBOX
+}
+
+
+/*! \internal
+
+ Sets the internal mode to \a m.
+
+*/
+
+void Q3DataManager::setMode(QSql::Op m)
+{
+ d->mode = m;
+}
+
+
+/*! \internal
+
+ Returns the current mode.
+
+*/
+
+QSql::Op Q3DataManager::mode() const
+{
+ return d->mode;
+}
+
+
+/*! \internal
+
+ Sets the auto-edit mode to \a auto.
+
+*/
+
+void Q3DataManager::setAutoEdit(bool autoEdit)
+{
+ d->autoEd = autoEdit;
+}
+
+
+
+/*! \internal
+
+ Returns true if auto-edit mode is enabled; otherwise returns false.
+
+*/
+
+bool Q3DataManager::autoEdit() const
+{
+ return d->autoEd;
+}
+
+/*! \internal
+
+ If \a confirm is true, all edit operations (inserts, updates and
+ deletes) will be confirmed by the user. If \a confirm is false (the
+ default), all edits are posted to the database immediately.
+
+*/
+void Q3DataManager::setConfirmEdits(bool confirm)
+{
+ d->confEdits = QBitArray(d->confEdits.size(), confirm);
+}
+
+/*! \internal
+
+ If \a confirm is true, all inserts will be confirmed by the user.
+ If \a confirm is false (the default), all edits are posted to the
+ database immediately.
+
+*/
+
+void Q3DataManager::setConfirmInsert(bool confirm)
+{
+ d->confEdits[QSql::Insert] = confirm;
+}
+
+/*! \internal
+
+ If \a confirm is true, all updates will be confirmed by the user.
+ If \a confirm is false (the default), all edits are posted to the
+ database immediately.
+
+*/
+
+void Q3DataManager::setConfirmUpdate(bool confirm)
+{
+ d->confEdits[QSql::Update] = confirm;
+}
+
+/*! \internal
+
+ If \a confirm is true, all deletes will be confirmed by the user.
+ If \a confirm is false (the default), all edits are posted to the
+ database immediately.
+
+*/
+
+void Q3DataManager::setConfirmDelete(bool confirm)
+{
+ d->confEdits[QSql::Delete] = confirm;
+}
+
+/*! \internal
+
+ Returns true if the table confirms all edit operations (inserts,
+ updates and deletes), otherwise returns false.
+*/
+
+bool Q3DataManager::confirmEdits() const
+{
+ return (confirmInsert() && confirmUpdate() && confirmDelete());
+}
+
+/*! \internal
+
+ Returns true if the table confirms inserts, otherwise returns
+ false.
+*/
+
+bool Q3DataManager::confirmInsert() const
+{
+ return d->confEdits[QSql::Insert];
+}
+
+/*! \internal
+
+ Returns true if the table confirms updates, otherwise returns
+ false.
+*/
+
+bool Q3DataManager::confirmUpdate() const
+{
+ return d->confEdits[QSql::Update];
+}
+
+/*! \internal
+
+ Returns true if the table confirms deletes, otherwise returns
+ false.
+*/
+
+bool Q3DataManager::confirmDelete() const
+{
+ return d->confEdits[QSql::Delete];
+}
+
+/*! \internal
+
+ If \a confirm is true, all cancels will be confirmed by the user
+ through a message box. If \a confirm is false (the default), all
+ cancels occur immediately.
+*/
+
+void Q3DataManager::setConfirmCancels(bool confirm)
+{
+ d->confCancs = confirm;
+}
+
+/*! \internal
+
+ Returns true if the table confirms cancels, otherwise returns false.
+*/
+
+bool Q3DataManager::confirmCancels() const
+{
+ return d->confCancs;
+}
+
+/*! \internal
+
+ Virtual function which returns a confirmation for an edit of mode \a
+ m. Derived classes can reimplement this function and provide their
+ own confirmation dialog. The default implementation uses a message
+ box which prompts the user to confirm the edit action. The dialog
+ is centered over \a parent.
+
+*/
+
+QSql::Confirm Q3DataManager::confirmEdit(QWidget* parent, QSql::Op m)
+{
+ int ans = 2;
+ if (m == QSql::Delete) {
+#ifndef QT_NO_MESSAGEBOX
+ ans = QMessageBox::information(parent,
+ qApp->translate("QSql", "Delete"),
+ qApp->translate("QSql", "Delete this record?"),
+ qApp->translate("QSql", "Yes"),
+ qApp->translate("QSql", "No"),
+ QString(), 0, 1);
+#else
+ ans = QSql::No;
+#endif // QT_NO_MESSAGEBOX
+ } else if (m != QSql::None) {
+ QString caption;
+ if (m == QSql::Insert) {
+ caption = qApp->translate("QSql", "Insert");
+ } else { // QSql::Update
+ caption = qApp->translate("QSql", "Update");
+ }
+#ifndef QT_NO_MESSAGEBOX
+ ans = QMessageBox::information(parent, caption,
+ qApp->translate("QSql", "Save edits?"),
+ qApp->translate("QSql", "Yes"),
+ qApp->translate("QSql", "No"),
+ qApp->translate("QSql", "Cancel"),
+ 0, 2);
+#else
+ ans = QSql::No;
+#endif // QT_NO_MESSAGEBOX
+ }
+
+ switch (ans) {
+ case 0:
+ return QSql::Yes;
+ case 1:
+ return QSql::No;
+ default:
+ return QSql::Cancel;
+ }
+}
+
+/*! \internal
+
+ Virtual function which returns a confirmation for canceling an edit
+ mode \a m. Derived classes can reimplement this function and
+ provide their own confirmation dialog. The default implementation
+ uses a message box which prompts the user to confirm the edit
+ action. The dialog is centered over \a parent.
+
+
+*/
+
+QSql::Confirm Q3DataManager::confirmCancel(QWidget* parent, QSql::Op)
+{
+#ifndef QT_NO_MESSAGEBOX
+ switch (QMessageBox::information(parent,
+ qApp->translate("QSql", "Confirm"),
+ qApp->translate("QSql", "Cancel your edits?"),
+ qApp->translate("QSql", "Yes"),
+ qApp->translate("QSql", "No"),
+ QString(), 0, 1)) {
+ case 0:
+ return QSql::Yes;
+ case 1:
+ return QSql::No;
+ default:
+ return QSql::Cancel;
+ }
+#else
+ return QSql::Yes;
+#endif // QT_NO_MESSAGEBOX
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/sql/q3sqlmanager_p.h b/src/qt3support/sql/q3sqlmanager_p.h
new file mode 100644
index 0000000..ada79f1
--- /dev/null
+++ b/src/qt3support/sql/q3sqlmanager_p.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLMANAGER_P_H
+#define Q3SQLMANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qvariant.h"
+#include "QtCore/qglobal.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
+#include "QtSql/qsql.h"
+#include "QtSql/qsqlerror.h"
+#include "QtSql/qsqlindex.h"
+#include "Qt3Support/q3sqlcursor.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SQL
+
+class Q3SqlCursor;
+class Q3SqlForm;
+class Q3SqlCursorManagerPrivate;
+
+class Q_COMPAT_EXPORT Q3SqlCursorManager
+{
+public:
+ Q3SqlCursorManager();
+ virtual ~Q3SqlCursorManager();
+
+ virtual void setSort(const QSqlIndex& sort);
+ virtual void setSort(const QStringList& sort);
+ QStringList sort() const;
+ virtual void setFilter(const QString& filter);
+ QString filter() const;
+ virtual void setCursor(Q3SqlCursor* cursor, bool autoDelete = false);
+ Q3SqlCursor* cursor() const;
+
+ virtual void setAutoDelete(bool enable);
+ bool autoDelete() const;
+
+ virtual bool refresh();
+ virtual bool findBuffer(const QSqlIndex& idx, int atHint = 0);
+
+private:
+ Q3SqlCursorManagerPrivate* d;
+};
+
+#ifndef QT_NO_SQL_FORM
+
+class Q3SqlFormManagerPrivate;
+
+class Q_COMPAT_EXPORT Q3SqlFormManager
+{
+public:
+ Q3SqlFormManager();
+ virtual ~Q3SqlFormManager();
+
+ virtual void setForm(Q3SqlForm* form);
+ Q3SqlForm* form();
+ virtual void setRecord(QSqlRecord* record);
+ QSqlRecord* record();
+
+ virtual void clearValues();
+ virtual void readFields();
+ virtual void writeFields();
+
+private:
+ Q3SqlFormManagerPrivate* d;
+};
+
+#endif
+
+class QWidget;
+class Q3DataManagerPrivate;
+
+class Q_COMPAT_EXPORT Q3DataManager
+{
+public:
+ Q3DataManager();
+ virtual ~Q3DataManager();
+
+ virtual void setMode(QSql::Op m);
+ QSql::Op mode() const;
+ virtual void setAutoEdit(bool autoEdit);
+ bool autoEdit() const;
+
+ virtual void handleError(QWidget* parent, const QSqlError& error);
+ virtual QSql::Confirm confirmEdit(QWidget* parent, QSql::Op m);
+ virtual QSql::Confirm confirmCancel(QWidget* parent, QSql::Op m);
+
+ virtual void setConfirmEdits(bool confirm);
+ virtual void setConfirmInsert(bool confirm);
+ virtual void setConfirmUpdate(bool confirm);
+ virtual void setConfirmDelete(bool confirm);
+ virtual void setConfirmCancels(bool confirm);
+
+ bool confirmEdits() const;
+ bool confirmInsert() const;
+ bool confirmUpdate() const;
+ bool confirmDelete() const;
+ bool confirmCancels() const;
+
+private:
+ Q3DataManagerPrivate* d;
+};
+
+#endif // QT_NO_SQL
+
+QT_END_NAMESPACE
+
+#endif // Q3SQLMANAGER_P_H
diff --git a/src/qt3support/sql/q3sqlpropertymap.cpp b/src/qt3support/sql/q3sqlpropertymap.cpp
new file mode 100644
index 0000000..02976dc
--- /dev/null
+++ b/src/qt3support/sql/q3sqlpropertymap.cpp
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3sqlpropertymap.h"
+
+#ifndef QT_NO_SQL_FORM
+
+#include "qwidget.h"
+#include "q3cleanuphandler.h"
+#include "qmetaobject.h"
+#include "qmap.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SqlPropertyMapPrivate
+{
+public:
+ Q3SqlPropertyMapPrivate() {}
+ QMap<QByteArray, QByteArray> propertyMap;
+};
+
+/*!
+ \class Q3SqlPropertyMap
+ \brief The Q3SqlPropertyMap class is used to map widgets to SQL fields.
+
+ \compat
+
+ The SQL module uses Qt \link properties.html object
+ properties\endlink to insert and extract values from editor
+ widgets.
+
+ This class is used to map editors to SQL fields. This works by
+ associating SQL editor class names to the properties used to
+ insert and extract values to/from the editor.
+
+ For example, a QLineEdit can be used to edit text strings and
+ other data types in Q3DataTables or Q3SqlForms. Several properties
+ are defined in QLineEdit, but only the \e text property is used to
+ insert and extract text from a QLineEdit. Both Q3DataTable and
+ Q3SqlForm use the global Q3SqlPropertyMap for inserting and
+ extracting values to and from an editor widget. The global
+ property map defines several common widgets and properties that
+ are suitable for many applications. You can add and remove widget
+ properties to suit your specific needs.
+
+ If you want to use custom editors with your Q3DataTable or
+ Q3SqlForm, you must install your own Q3SqlPropertyMap for that table
+ or form. Example:
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlpropertymap.cpp 0
+
+ You can also replace the global Q3SqlPropertyMap that is used by
+ default. (Bear in mind that Q3SqlPropertyMap takes ownership of the
+ new default map.)
+
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlpropertymap.cpp 1
+
+ \sa Q3DataTable, Q3SqlForm, Q3SqlEditorFactory
+*/
+
+/*!
+
+Constructs a Q3SqlPropertyMap.
+
+The default property mappings used by Qt widgets are:
+\table
+\header \i Widgets \i Property
+\row \i \l QCheckBox,
+ \l QRadioButton
+ \i checked
+\row \i \l QComboBox,
+ \l Q3ListBox
+ \i currentItem
+\row \i \l Q3DateEdit
+ \i date
+\row \i \l Q3DateTimeEdit
+ \l QDateTimeEdit
+ \i dateTime
+\row \i \l QTextBrowser
+ \i source
+\row \i \l QAbstractButton,
+ \l QDial,
+ \l QLabel,
+ \l QLineEdit,
+ \l Q3MultiLineEdit,
+ \l QPushButton,
+ \l QTextEdit,
+ \i text
+\row \i \l Q3TimeEdit
+ \i time
+\row \i \l QLCDNumber,
+ \l QScrollBar
+ \l QSlider,
+ \l QSpinBox
+ \i value
+\endtable
+*/
+
+Q3SqlPropertyMap::Q3SqlPropertyMap()
+{
+ d = new Q3SqlPropertyMapPrivate();
+ const struct MapData {
+ const char *classname;
+ const char *property;
+ } mapData[] = {
+ { "Q3DateEdit", "date" },
+ { "Q3DateTimeEdit", "dateTime" },
+ { "Q3ListBox", "currentItem" },
+ { "Q3TimeEdit", "time" },
+ { "QAbstractButton", "text" },
+ { "QCheckBox", "checked" },
+ { "QRadioButton", "checked" },
+ { "QComboBox", "currentIndex" },
+ { "QDateTimeEdit", "dateTime" },
+ { "QDial", "value" },
+ { "QLabel", "text" },
+ { "QLCDNumber", "value" },
+ { "QLineEdit", "text" },
+ { "QPushButton", "text" },
+ { "QScrollBar", "value" },
+ { "QSlider", "value" },
+ { "QSpinBox", "value" },
+ { "QTabBar", "currentTab" },
+ { "QTabWidget", "currentPage" },
+ { "QTextBrowser", "source" },
+ { "QTextEdit", "text" },
+ { "QGroupBox", "checked" }
+ };
+
+ const MapData *m = mapData;
+ for (uint i = 0; i < sizeof(mapData)/sizeof(MapData); i++, m++)
+ d->propertyMap.insert(m->classname, m->property);
+}
+
+/*!
+ Destroys the Q3SqlPropertyMap.
+
+ Note that if the Q3SqlPropertyMap is installed with
+ installPropertyMap() the object it was installed into, e.g. the
+ Q3SqlForm, takes ownership and will delete the Q3SqlPropertyMap when
+ necessary.
+*/
+Q3SqlPropertyMap::~Q3SqlPropertyMap()
+{
+ delete d;
+}
+
+/*!
+ Returns the mapped property of \a widget as a QVariant.
+*/
+QVariant Q3SqlPropertyMap::property(QWidget * widget)
+{
+ if(!widget) return QVariant();
+ const QMetaObject* mo = widget->metaObject();
+ while (mo && !d->propertyMap.contains(mo->className()))
+ mo = mo->superClass();
+
+ if (!mo) {
+ qWarning("Q3SqlPropertyMap::property: %s does not exist", widget->metaObject()->className());
+ return QVariant();
+ }
+ return widget->property(d->propertyMap[mo->className()]);
+}
+
+/*!
+ Sets the property of \a widget to \a value.
+*/
+void Q3SqlPropertyMap::setProperty(QWidget * widget, const QVariant & value)
+{
+ if(!widget) return;
+
+ const QMetaObject* mo = widget->metaObject();
+ while (mo && !d->propertyMap.contains(mo->className()))
+ mo = mo->superClass();
+ if (!mo) {
+ qWarning("Q3SqlPropertyMap::setProperty: %s not handled by Q3SqlPropertyMap", widget->metaObject()->className());
+ return;
+ }
+
+ widget->setProperty(d->propertyMap[mo->className()], value);
+}
+
+/*!
+ Insert a new classname/property pair, which is used for custom SQL
+ field editors. There \e must be a Q_PROPERTY() clause in the \a
+ classname class declaration for the \a property.
+*/
+void Q3SqlPropertyMap::insert(const QString & classname,
+ const QString & property)
+{
+ d->propertyMap[classname.latin1()] = property.latin1();
+}
+
+/*!
+ Removes \a classname from the map.
+*/
+void Q3SqlPropertyMap::remove(const QString & classname)
+{
+ d->propertyMap.remove(classname.latin1());
+}
+
+static Q3SqlPropertyMap * defaultmap = 0;
+static Q3CleanupHandler< Q3SqlPropertyMap > qsql_cleanup_property_map;
+
+/*!
+ Returns the application global Q3SqlPropertyMap.
+*/
+Q3SqlPropertyMap * Q3SqlPropertyMap::defaultMap()
+{
+ if(defaultmap == 0){
+ defaultmap = new Q3SqlPropertyMap();
+ qsql_cleanup_property_map.add(&defaultmap);
+ }
+ return defaultmap;
+}
+
+/*!
+ Replaces the global default property map with \a map. All
+ Q3DataTable and Q3SqlForm instantiations will use this new map for
+ inserting and extracting values to and from editors.
+ \e{Q3SqlPropertyMap takes ownership of \a map, and destroys it
+ when it is no longer needed.}
+*/
+void Q3SqlPropertyMap::installDefaultMap(Q3SqlPropertyMap * map)
+{
+ if(map == 0) return;
+
+ if(defaultmap != 0){
+ qsql_cleanup_property_map.remove(&defaultmap);
+ delete defaultmap;
+ }
+ defaultmap = map;
+ qsql_cleanup_property_map.add(&defaultmap);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SQL_FORM
diff --git a/src/qt3support/sql/q3sqlpropertymap.h b/src/qt3support/sql/q3sqlpropertymap.h
new file mode 100644
index 0000000..3d45111
--- /dev/null
+++ b/src/qt3support/sql/q3sqlpropertymap.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLPROPERTYMAP_H
+#define Q3SQLPROPERTYMAP_H
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL_FORM
+
+class QWidget;
+class Q3SqlPropertyMapPrivate;
+
+class Q_COMPAT_EXPORT Q3SqlPropertyMap
+{
+public:
+ Q3SqlPropertyMap();
+ virtual ~Q3SqlPropertyMap();
+
+ QVariant property(QWidget * widget);
+ virtual void setProperty(QWidget * widget, const QVariant & value);
+
+ void insert(const QString & classname, const QString & property);
+ void remove(const QString & classname);
+
+ static Q3SqlPropertyMap * defaultMap();
+ static void installDefaultMap(Q3SqlPropertyMap * map);
+
+private:
+ Q_DISABLE_COPY(Q3SqlPropertyMap)
+
+ Q3SqlPropertyMapPrivate* d;
+};
+
+#endif // QT_NO_SQL_FORM
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SQLPROPERTYMAP_H
diff --git a/src/qt3support/sql/q3sqlrecordinfo.h b/src/qt3support/sql/q3sqlrecordinfo.h
new file mode 100644
index 0000000..c4a56a8
--- /dev/null
+++ b/src/qt3support/sql/q3sqlrecordinfo.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLRECORDINFO_H
+#define Q3SQLRECORDINFO_H
+
+#include <QtCore/qglobal.h>
+
+#ifndef QT_NO_SQL
+# include <Qt3Support/q3valuelist.h>
+# include <QtSql/qsqlrecord.h>
+# include <Qt3Support/q3sqlfieldinfo.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL
+
+/* Q3SqlRecordInfo Class
+ This class is obsolete, use QSqlRecord instead.
+*/
+
+typedef Q3ValueList<Q3SqlFieldInfo> Q3SqlFieldInfoList;
+
+class Q_COMPAT_EXPORT Q3SqlRecordInfo: public Q3SqlFieldInfoList
+{
+public:
+ Q3SqlRecordInfo(): Q3SqlFieldInfoList() {}
+ Q3SqlRecordInfo(const Q3SqlFieldInfoList& other): Q3SqlFieldInfoList(other) {}
+ Q3SqlRecordInfo(const QSqlRecord& other)
+ {
+ for (int i = 0; i < other.count(); ++i)
+ push_back(Q3SqlFieldInfo(other.field(i)));
+ }
+
+ size_type contains(const QString& fieldName) const;
+ Q3SqlFieldInfo find(const QString& fieldName) const;
+ QSqlRecord toRecord() const;
+};
+
+inline Q3SqlRecordInfo::size_type Q3SqlRecordInfo::contains(const QString& fieldName) const
+{
+ size_type i = 0;
+ QString fName = fieldName.toUpper();
+
+ for(const_iterator it = begin(); it != end(); ++it) {
+ if ((*it).name().toUpper() == fName) {
+ ++i;
+ }
+ }
+ return i;
+}
+
+inline Q3SqlFieldInfo Q3SqlRecordInfo::find(const QString& fieldName) const
+{
+ QString fName = fieldName.toUpper();
+ for(const_iterator it = begin(); it != end(); ++it) {
+ if ((*it).name().toUpper() == fName) {
+ return *it;
+ }
+ }
+ return Q3SqlFieldInfo();
+}
+
+inline QSqlRecord Q3SqlRecordInfo::toRecord() const
+{
+ QSqlRecord buf;
+ for(const_iterator it = begin(); it != end(); ++it) {
+ buf.append((*it).toField());
+ }
+ return buf;
+}
+
+#endif // QT_NO_SQL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SQLRECORDINFO_H
diff --git a/src/qt3support/sql/q3sqlrecordinfo.qdoc b/src/qt3support/sql/q3sqlrecordinfo.qdoc
new file mode 100644
index 0000000..9685bc8
--- /dev/null
+++ b/src/qt3support/sql/q3sqlrecordinfo.qdoc
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3SqlRecordInfo
+ \brief The Q3SqlRecordInfo class encapsulates a set of database field meta data.
+
+ \compat
+
+ This class is a list that holds a set of database field meta
+ data. Use contains() to see if a given field name exists in the
+ record, and use find() to get a QSqlFieldInfo record for a named
+ field.
+
+ \sa Q3SqlFieldInfo
+*/
+
+/*!
+ \fn Q3SqlRecordInfo::Q3SqlRecordInfo()
+
+ Constructs an empty record info object.
+*/
+
+/*!
+ \fn Q3SqlRecordInfo::Q3SqlRecordInfo(const Q3SqlFieldInfoList& other)
+ \fn Q3SqlRecordInfo::Q3SqlRecordInfo(const QSqlRecord& other)
+
+ Constructs a copy of \a other.
+*/
+
+/*!
+ \fn size_type Q3SqlRecordInfo::contains(const QString& fieldName) const
+
+ Returns the number of times a field called \a fieldName occurs in
+ the record. Returns 0 if no field by that name could be found.
+*/
+
+/*!
+ \fn Q3SqlFieldInfo Q3SqlRecordInfo::find(const QString& fieldName) const
+
+ Returns a QSqlFieldInfo object for the first field in the record
+ which has the field name \a fieldName. If no matching field is
+ found then an empty QSqlFieldInfo object is returned.
+*/
+
+/*!
+ \fn QSqlRecord Q3SqlRecordInfo::toRecord() const
+
+ Returns an empty QSqlRecord based on the field information
+ in this Q3SqlRecordInfo.
+*/
diff --git a/src/qt3support/sql/q3sqlselectcursor.cpp b/src/qt3support/sql/q3sqlselectcursor.cpp
new file mode 100644
index 0000000..a273f98
--- /dev/null
+++ b/src/qt3support/sql/q3sqlselectcursor.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3sqlselectcursor.h"
+#include "qsqldriver.h"
+#include "q3sqlrecordinfo.h"
+
+#ifndef QT_NO_SQL
+
+QT_BEGIN_NAMESPACE
+
+class Q3SqlSelectCursorPrivate
+{
+public:
+ Q3SqlSelectCursorPrivate() : populated(false) {}
+ QString query;
+ bool populated : 1;
+};
+
+/*!
+ \class Q3SqlSelectCursor
+ \brief The Q3SqlSelectCursor class provides browsing of general SQL SELECT statements.
+
+ \compat
+
+ Q3SqlSelectCursor is a convenience class that makes it possible to
+ display result sets from general SQL \c SELECT statements in
+ data-aware Qt widgets. Q3SqlSelectCursor is read-only and does not
+ support \c INSERT, \c UPDATE or \c DELETE operations.
+
+ Pass the query in at construction time, or use the
+ Q3SqlSelectCursor::exec() function.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlselectcursor.cpp 0
+*/
+
+/*!
+ Constructs a read only cursor on database \a db using the query \a query.
+ */
+Q3SqlSelectCursor::Q3SqlSelectCursor(const QString& query, QSqlDatabase db)
+ : Q3SqlCursor(QString(), false, db)
+{
+ d = new Q3SqlSelectCursorPrivate;
+ d->query = query;
+ Q3SqlCursor::setMode(ReadOnly);
+ if (!query.isEmpty())
+ exec(query);
+}
+
+/*! Constructs a copy of \a other */
+Q3SqlSelectCursor::Q3SqlSelectCursor(const Q3SqlSelectCursor& other)
+ : Q3SqlCursor(other)
+{
+ d = new Q3SqlSelectCursorPrivate;
+ d->query = other.d->query;
+ d->populated = other.d->populated;
+}
+
+/*! Destroys the object and frees any allocated resources */
+Q3SqlSelectCursor::~Q3SqlSelectCursor()
+{
+ delete d;
+}
+
+/*! \internal */
+bool Q3SqlSelectCursor::exec(const QString& query)
+{
+ d->query = query;
+ bool ret = Q3SqlCursor::exec(query);
+ if (ret) {
+ Q3SqlCursor::clear();
+ populateCursor();
+ }
+ return ret;
+}
+
+/*! \fn bool Q3SqlSelectCursor::select()
+ \internal
+*/
+
+/*! \internal */
+bool Q3SqlSelectCursor::select(const QString&, const QSqlIndex&)
+{
+ bool ret = Q3SqlCursor::exec(d->query);
+ if (ret && !d->populated)
+ populateCursor();
+ return ret;
+}
+
+/*! \internal */
+void Q3SqlSelectCursor::populateCursor()
+{
+ Q3SqlRecordInfo inf = Q3SqlRecordInfo(record());
+ for (Q3SqlRecordInfo::const_iterator it = inf.begin(); it != inf.end(); ++it)
+ Q3SqlCursor::append(*it);
+ d->populated = true;
+}
+
+/*! \fn QSqlIndex Q3SqlSelectCursor::primaryIndex(bool) const
+ \internal
+*/
+
+/*! \fn QSqlIndex Q3SqlSelectCursor::index(const QStringList&) const
+ \internal
+*/
+
+/*! \fn QSqlIndex Q3SqlSelectCursor::index(const QString&) const
+ \internal
+*/
+
+/*! \fn QSqlIndex Q3SqlSelectCursor::index(const char*) const
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::setPrimaryIndex(const QSqlIndex&)
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::append(const Q3SqlFieldInfo&)
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::insert(int, const Q3SqlFieldInfo&)
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::remove(int)
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::clear()
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::setGenerated(const QString&, bool)
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::setGenerated(int, bool)
+ \internal
+*/
+
+/*! \fn QSqlRecord* Q3SqlSelectCursor::editBuffer(bool)
+ \internal
+*/
+
+/*! \fn QSqlRecord* Q3SqlSelectCursor::primeInsert()
+ \internal
+*/
+
+/*! \fn QSqlRecord* Q3SqlSelectCursor::primeUpdate()
+ \internal
+*/
+
+/*! \fn QSqlRecord* Q3SqlSelectCursor::primeDelete()
+ \internal
+*/
+
+/*! \fn int Q3SqlSelectCursor::insert(bool)
+ \internal
+*/
+
+/*! \fn int Q3SqlSelectCursor::update(bool)
+ \internal
+*/
+
+/*! \fn int Q3SqlSelectCursor::del(bool)
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::setMode(int)
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::setSort(const QSqlIndex&)
+ \internal
+*/
+
+/*! \fn QSqlIndex Q3SqlSelectCursor::sort() const
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::setFilter(const QString&)
+ \internal
+*/
+
+/*! \fn QString Q3SqlSelectCursor::filter() const
+ \internal
+*/
+
+/*! \fn void Q3SqlSelectCursor::setName(const QString&, bool)
+ \internal
+*/
+
+/*! \fn QString Q3SqlSelectCursor::name() const
+ \internal
+*/
+
+/*! \fn QString Q3SqlSelectCursor::toString(const QString&, const QString&) const
+ \internal
+*/
+
+/*!
+ \fn int Q3SqlSelectCursor::update(const QString & filter, bool invalidate = true)
+ \overload
+
+ Updates the database with the current contents of the cursor edit
+ buffer using the specified \a filter. Returns the number of
+ records which were updated.
+ For error information, use lastError().
+
+ Only records which meet the filter criteria are updated, otherwise
+ all records in the table are updated.
+
+ If \a invalidate is true (the default), the cursor can no longer
+ be navigated. A new select() call must be made before you can move
+ to a valid record.
+
+ \sa Q3SqlCursor::update() primeUpdate() setMode() lastError()
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SQL
diff --git a/src/qt3support/sql/q3sqlselectcursor.h b/src/qt3support/sql/q3sqlselectcursor.h
new file mode 100644
index 0000000..36bc34f
--- /dev/null
+++ b/src/qt3support/sql/q3sqlselectcursor.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SQLSELECTCURSOR_H
+#define Q3SQLSELECTCURSOR_H
+
+#include <Qt3Support/q3sqlcursor.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_SQL
+
+class Q3SqlSelectCursorPrivate;
+
+class Q_COMPAT_EXPORT Q3SqlSelectCursor : public Q3SqlCursor
+{
+public:
+ Q3SqlSelectCursor(const QString& query = QString(), QSqlDatabase db = QSqlDatabase());
+ Q3SqlSelectCursor(const Q3SqlSelectCursor& other);
+ ~Q3SqlSelectCursor();
+ bool exec(const QString& query);
+ bool select() { return Q3SqlCursor::select(); }
+
+protected:
+ QSqlIndex primaryIndex(bool = true) const { return QSqlIndex(); }
+ QSqlIndex index(const QStringList&) const { return QSqlIndex(); }
+ QSqlIndex index(const QString&) const { return QSqlIndex(); }
+ QSqlIndex index(const char*) const { return QSqlIndex(); }
+ void setPrimaryIndex(const QSqlIndex&) {}
+ void append(const Q3SqlFieldInfo&) {}
+ void insert(int, const Q3SqlFieldInfo&) {}
+ void remove(int) {}
+ void clear() {}
+ void setGenerated(const QString&, bool) {}
+ void setGenerated(int, bool) {}
+ QSqlRecord* editBuffer(bool = false) { return 0; }
+ QSqlRecord* primeInsert() { return 0; }
+ QSqlRecord* primeUpdate() { return 0; }
+ QSqlRecord* primeDelete() { return 0; }
+ int insert(bool = true) { return 0; }
+ int update(bool = true) { return 0; }
+ int del(bool = true) { return 0; }
+ void setMode(int) {}
+
+ void setSort(const QSqlIndex&) {}
+ QSqlIndex sort() const { return QSqlIndex(); }
+ void setFilter(const QString&) {}
+ QString filter() const { return QString(); }
+ void setName(const QString&, bool = true) {}
+ QString name() const { return QString(); }
+ QString toString(const QString& = QString(), const QString& = QLatin1String(",")) const { return QString(); }
+ bool select(const QString &, const QSqlIndex& = QSqlIndex());
+
+private:
+ void populateCursor();
+
+ Q3SqlSelectCursorPrivate * d;
+
+protected:
+#if !defined(Q_NO_USING_KEYWORD)
+ using Q3SqlCursor::update;
+#else
+ virtual int update(const QString & filter, bool invalidate = true) { return Q3SqlCursor::update(filter, invalidate); }
+#endif
+};
+
+#endif // QT_NO_SQL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SQLSELECTCURSOR_H
diff --git a/src/qt3support/sql/sql.pri b/src/qt3support/sql/sql.pri
new file mode 100644
index 0000000..41fd641
--- /dev/null
+++ b/src/qt3support/sql/sql.pri
@@ -0,0 +1,25 @@
+# Qt compat module
+
+HEADERS += sql/q3sqlfieldinfo.h \
+ sql/q3sqlrecordinfo.h \
+ sql/q3datatable.h \
+ sql/q3dataview.h \
+ sql/q3sqlcursor.h \
+ sql/q3sqlselectcursor.h \
+ sql/q3sqlform.h \
+ sql/q3sqlmanager_p.h \
+ sql/q3editorfactory.h \
+ sql/q3sqleditorfactory.h \
+ sql/q3sqlpropertymap.h \
+ sql/q3databrowser.h \
+
+SOURCES += sql/q3datatable.cpp \
+ sql/q3dataview.cpp \
+ sql/q3sqlcursor.cpp \
+ sql/q3sqlselectcursor.cpp \
+ sql/q3sqlform.cpp \
+ sql/q3sqlmanager_p.cpp \
+ sql/q3editorfactory.cpp \
+ sql/q3sqleditorfactory.cpp \
+ sql/q3sqlpropertymap.cpp \
+ sql/q3databrowser.cpp
diff --git a/src/qt3support/text/q3multilineedit.cpp b/src/qt3support/text/q3multilineedit.cpp
new file mode 100644
index 0000000..6c00ed8
--- /dev/null
+++ b/src/qt3support/text/q3multilineedit.cpp
@@ -0,0 +1,535 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qplatformdefs.h>
+#include "q3multilineedit.h"
+#ifndef QT_NO_MULTILINEEDIT
+#include "qpainter.h"
+#include "qscrollbar.h"
+#include "qcursor.h"
+#include "qclipboard.h"
+#include "qpixmap.h"
+#include "qregexp.h"
+#include "qapplication.h"
+#include "q3dragobject.h"
+#include "qtimer.h"
+#include <private/q3richtext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3MultiLineEdit
+
+ \brief The Q3MultiLineEdit widget is a simple editor for inputting text.
+
+ \compat
+
+ The Q3MultiLineEdit was a simple editor widget in former Qt versions. Qt
+ 3.0 includes a new richtext engine which obsoletes Q3MultiLineEdit. It is
+ still included for compatibility reasons. It is now a subclass of
+ \l Q3TextEdit, and provides enough of the old Q3MultiLineEdit API to keep old
+ applications working.
+
+ If you implement something new with Q3MultiLineEdit, we suggest using
+ \l Q3TextEdit instead and call Q3TextEdit::setTextFormat(Qt::PlainText).
+
+ Although most of the old Q3MultiLineEdit API is still available, there is
+ a few difference. The old Q3MultiLineEdit operated on lines, not on
+ paragraphs. As lines change all the time during wordwrap, the new
+ richtext engine uses paragraphs as basic elements in the data structure.
+ All functions (numLines(), textLine(), etc.) that operated on lines, now
+ operate on paragraphs. Further, getString() has been removed completely.
+ It revealed too much of the internal data structure.
+
+ Applications which made normal and reasonable use of Q3MultiLineEdit
+ should still work without problems. Some odd usage will require some
+ porting. In these cases, it may be better to use \l Q3TextEdit now.
+
+ \sa Q3TextEdit
+*/
+
+/*!
+ \fn bool Q3MultiLineEdit::autoUpdate() const
+
+ This function is a noop that always returns true.
+*/
+
+/*!
+ \fn virtual void Q3MultiLineEdit::setAutoUpdate(bool b)
+
+ \internal
+*/
+
+/*!
+ \fn int Q3MultiLineEdit::totalWidth() const
+*/
+
+/*!
+ \fn int Q3MultiLineEdit::totalHeight() const
+*/
+
+/*!
+ \fn int Q3MultiLineEdit::maxLines() const
+*/
+
+/*!
+ \fn void Q3MultiLineEdit::setMaxLines(int max)
+
+ Sets the maximum number of lines this Q3MultiLineEdit will hold to
+ \a max.
+*/
+
+/*!
+ \fn void Q3MultiLineEdit::deselect()
+*/
+
+
+class Q3MultiLineEditData
+{
+};
+
+
+/*!
+ Constructs a new, empty, Q3MultiLineEdit with parent \a parent called
+ \a name.
+*/
+
+Q3MultiLineEdit::Q3MultiLineEdit(QWidget *parent , const char *name)
+ : Q3TextEdit(parent, name)
+{
+ d = new Q3MultiLineEditData;
+ setTextFormat(Qt::PlainText);
+}
+
+/*! \property Q3MultiLineEdit::numLines
+ \brief the number of paragraphs in the editor
+
+ The count includes any empty paragraph at top and bottom, so for an
+ empty editor this method returns 1.
+*/
+
+int Q3MultiLineEdit::numLines() const
+{
+ return document()->lastParagraph()->paragId() + 1;
+}
+
+/*! \property Q3MultiLineEdit::atEnd
+ \brief whether the cursor is placed at the end of the text
+
+ \sa atBeginning
+*/
+
+bool Q3MultiLineEdit::atEnd() const
+{
+ return textCursor()->paragraph() == document()->lastParagraph() && textCursor()->atParagEnd();
+}
+
+
+/*! \property Q3MultiLineEdit::atBeginning
+ \brief whether the cursor is placed at the beginning of the text
+
+ \sa atEnd
+*/
+
+bool Q3MultiLineEdit::atBeginning() const
+{
+ return textCursor()->paragraph() == document()->firstParagraph() && textCursor()->atParagStart();
+}
+
+/*! Returns the number of characters at paragraph number \a row. If
+ \a row is out of range, -1 is returned.
+*/
+
+int Q3MultiLineEdit::lineLength(int row) const
+{
+ if (row < 0 || row > numLines())
+ return -1;
+ return document()->paragAt(row)->length() - 1;
+}
+
+
+/*! Destructor. */
+
+Q3MultiLineEdit::~Q3MultiLineEdit()
+{
+ delete d;
+}
+
+/*!
+ If there is selected text, sets \a line1, \a col1, \a line2 and \a col2
+ to the start and end of the selected region and returns true. Returns
+ false if there is no selected text.
+ */
+bool Q3MultiLineEdit::getMarkedRegion(int *line1, int *col1,
+ int *line2, int *col2) const
+{
+ int p1,c1, p2, c2;
+ getSelection(&p1, &c1, &p2, &c2);
+ if (p1 == -1 && c1 == -1 && p2 == -1 && c2 == -1)
+ return false;
+ if (line1)
+ *line1 = p1;
+ if (col1)
+ *col1 = c1;
+ if (line2)
+ *line2 = p2;
+ if (col2)
+ *col2 = c2;
+ return true;
+}
+
+
+/*!
+ Returns true if there is selected text.
+*/
+
+bool Q3MultiLineEdit::hasMarkedText() const
+{
+ return hasSelectedText();
+}
+
+
+/*!
+ Returns a copy of the selected text.
+*/
+
+QString Q3MultiLineEdit::markedText() const
+{
+ return selectedText();
+}
+
+/*!
+ Moves the cursor one page down. If \a mark is true, the text
+ is selected.
+*/
+
+void Q3MultiLineEdit::pageDown(bool mark)
+{
+ moveCursor(MoveDown, mark);
+}
+
+
+/*!
+ Moves the cursor one page up. If \a mark is true, the text
+ is selected.
+*/
+
+void Q3MultiLineEdit::pageUp(bool mark)
+{
+ moveCursor(MovePgUp, mark);
+}
+
+
+/*! Inserts \a txt at paragraph number \a line. If \a line is less
+ than zero, or larger than the number of paragraphs, the new text is
+ put at the end. If \a txt contains newline characters, several
+ paragraphs are inserted.
+
+ The cursor position is not changed.
+*/
+
+void Q3MultiLineEdit::insertLine(const QString &txt, int line)
+{
+ insertParagraph(txt, line);
+}
+
+/*! Deletes the paragraph at paragraph number \a paragraph. If \a
+ paragraph is less than zero or larger than the number of paragraphs,
+ nothing is deleted.
+*/
+
+void Q3MultiLineEdit::removeLine(int paragraph)
+{
+ removeParagraph(paragraph);
+}
+
+/*! Inserts \a str at the current cursor position and selects the
+ text if \a mark is true.
+*/
+
+void Q3MultiLineEdit::insertAndMark(const QString& str, bool mark)
+{
+ insert(str);
+ if (mark)
+ document()->setSelectionEnd(Q3TextDocument::Standard, *textCursor());
+}
+
+/*! Splits the paragraph at the current cursor position.
+*/
+
+void Q3MultiLineEdit::newLine()
+{
+ insert(QString(QLatin1Char('\n')));
+}
+
+
+/*! Deletes the character on the left side of the text cursor and
+ moves the cursor one position to the left. If a text has been selected
+ by the user (e.g. by clicking and dragging) the cursor is put at the
+ beginning of the selected text and the selected text is removed. \sa
+ del()
+*/
+
+void Q3MultiLineEdit::backspace()
+{
+ if (document()->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ return;
+ }
+
+ if (!textCursor()->paragraph()->prev() &&
+ textCursor()->atParagStart())
+ return;
+
+ doKeyboardAction(ActionBackspace);
+}
+
+
+/*! Moves the text cursor to the left end of the line. If \a mark is
+ true, text is selected toward the first position. If it is false and the
+ cursor is moved, all selected text is unselected.
+
+ \sa end()
+*/
+
+void Q3MultiLineEdit::home(bool mark)
+{
+ moveCursor(MoveLineStart, mark);
+}
+
+/*! Moves the text cursor to the right end of the line. If \a mark is
+ true, text is selected toward the last position. If it is false and the
+ cursor is moved, all selected text is unselected.
+
+ \sa home()
+*/
+
+void Q3MultiLineEdit::end(bool mark)
+{
+ moveCursor(MoveLineEnd, mark);
+}
+
+
+/*!
+ \fn void Q3MultiLineEdit::setCursorPosition(int line, int col)
+ \reimp
+*/
+
+/*! Sets the cursor position to character number \a col in paragraph
+ number \a line. The parameters are adjusted to lie within the legal
+ range.
+
+ If \a mark is false, the selection is cleared. otherwise it is extended.
+
+*/
+
+void Q3MultiLineEdit::setCursorPosition(int line, int col, bool mark)
+{
+ if (!mark)
+ selectAll(false);
+ Q3TextEdit::setCursorPosition(line, col);
+ if (mark)
+ document()->setSelectionEnd(Q3TextDocument::Standard, *textCursor());
+}
+
+/*! Returns the top center point where the cursor is drawn.
+*/
+
+QPoint Q3MultiLineEdit::cursorPoint() const
+{
+ return QPoint(textCursor()->x(), textCursor()->y() + textCursor()->paragraph()->rect().y());
+}
+
+/*! \property Q3MultiLineEdit::alignment
+ \brief The editor's paragraph alignment
+
+ Sets the alignment to flag, which must be Qt::AlignLeft,
+ Qt::AlignHCenter, or \c Qt::AlignRight.
+
+ If flag is an illegal flag, nothing happens.
+*/
+void Q3MultiLineEdit::setAlignment(Qt::Alignment flag)
+{
+ if (flag == Qt::AlignCenter)
+ flag = Qt::AlignHCenter;
+ if (flag != Qt::AlignLeft && flag != Qt::AlignRight && flag != Qt::AlignHCenter)
+ return;
+ Q3TextParagraph *p = document()->firstParagraph();
+ while (p) {
+ p->setAlignment(flag);
+ p = p->next();
+ }
+}
+
+Qt::Alignment Q3MultiLineEdit::alignment() const
+{
+ return QFlag(document()->firstParagraph()->alignment());
+}
+
+
+void Q3MultiLineEdit::setEdited(bool e)
+{
+ setModified(e);
+}
+
+/*! \property Q3MultiLineEdit::edited
+ \brief whether the document has been edited by the user
+
+ This is the same as Q3TextEdit's "modifed" property.
+*/
+bool Q3MultiLineEdit::edited() const
+{
+ return isModified();
+}
+
+/*! Moves the cursor one word to the right. If \a mark is true, the text
+ is selected.
+
+ \sa cursorWordBackward()
+*/
+void Q3MultiLineEdit::cursorWordForward(bool mark)
+{
+ moveCursor(MoveWordForward, mark);
+}
+
+/*! Moves the cursor one word to the left. If \a mark is true, the
+ text is selected.
+
+ \sa cursorWordForward()
+*/
+void Q3MultiLineEdit::cursorWordBackward(bool mark)
+{
+ moveCursor(MoveWordBackward, mark);
+}
+
+/*!
+ \fn Q3MultiLineEdit::insertAt(const QString &s, int line, int col)
+ \reimp
+*/
+
+/*! Inserts string \a s at paragraph number \a line, after character
+ number \a col in the paragraph. If \a s contains newline
+ characters, new lines are inserted.
+ If \a mark is true the inserted string will be selected.
+
+ The cursor position is adjusted.
+ */
+
+void Q3MultiLineEdit::insertAt(const QString &s, int line, int col, bool mark)
+{
+ Q3TextEdit::insertAt(s, line, col);
+ if (mark)
+ setSelection(line, col, line, col + s.length());
+}
+
+// ### reggie - is this documentation correct?
+
+/*! Deletes text from the current cursor position to the end of the
+ line. (Note that this function still operates on lines, not paragraphs.)
+*/
+
+void Q3MultiLineEdit::killLine()
+{
+ doKeyboardAction(ActionKill);
+}
+
+/*! Moves the cursor one character to the left. If \a mark is true,
+ the text is selected.
+ The \a wrap parameter is currently ignored.
+
+ \sa cursorRight() cursorUp() cursorDown()
+*/
+
+void Q3MultiLineEdit::cursorLeft(bool mark, bool)
+{
+ moveCursor(MoveBackward, mark);
+}
+
+/*! Moves the cursor one character to the right. If \a mark is true,
+ the text is selected.
+ The \a wrap parameter is currently ignored.
+
+ \sa cursorLeft() cursorUp() cursorDown()
+*/
+
+void Q3MultiLineEdit::cursorRight(bool mark, bool)
+{
+ moveCursor(MoveForward, mark);
+}
+
+/*! Moves the cursor up one line. If \a mark is true, the text is
+ selected.
+
+ \sa cursorDown() cursorLeft() cursorRight()
+*/
+
+void Q3MultiLineEdit::cursorUp(bool mark)
+{
+ moveCursor(MoveUp, mark);
+}
+
+/*!
+ Moves the cursor one line down. If \a mark is true, the text
+ is selected.
+ \sa cursorUp() cursorLeft() cursorRight()
+*/
+
+void Q3MultiLineEdit::cursorDown(bool mark)
+{
+ moveCursor(MoveDown, mark);
+}
+
+
+/*! Returns the text at line number \a line (possibly the empty
+ string), or a null if \a line is invalid.
+*/
+
+QString Q3MultiLineEdit::textLine(int line) const
+{
+ if (line < 0 || line >= numLines())
+ return QString();
+ QString str = document()->paragAt(line)->string()->toString();
+ str.truncate(str.length() - 1);
+ return str;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/text/q3multilineedit.h b/src/qt3support/text/q3multilineedit.h
new file mode 100644
index 0000000..d3e6027
--- /dev/null
+++ b/src/qt3support/text/q3multilineedit.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3MULTILINEEDIT_H
+#define Q3MULTILINEEDIT_H
+
+#include <Qt3Support/q3textedit.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_MULTILINEEDIT
+
+class Q3MultiLineEditCommand;
+class QValidator;
+class Q3MultiLineEditData;
+
+class Q_COMPAT_EXPORT Q3MultiLineEdit : public Q3TextEdit
+{
+ Q_OBJECT
+ Q_PROPERTY(int numLines READ numLines)
+ Q_PROPERTY(bool atBeginning READ atBeginning)
+ Q_PROPERTY(bool atEnd READ atEnd)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(bool edited READ edited WRITE setEdited DESIGNABLE false)
+
+public:
+ Q3MultiLineEdit(QWidget* parent=0, const char* name=0);
+ ~Q3MultiLineEdit();
+
+ QString textLine(int line) const;
+ int numLines() const;
+
+ virtual void insertLine(const QString &s, int line = -1);
+ virtual void insertAt(const QString &s, int line, int col) {
+ insertAt(s, line, col, false);
+ }
+ virtual void insertAt(const QString &s, int line, int col, bool mark);
+ virtual void removeLine(int line);
+ virtual void setCursorPosition(int line, int col) {
+ setCursorPosition(line, col, false);
+ }
+ virtual void setCursorPosition(int line, int col, bool mark);
+ bool atBeginning() const;
+ bool atEnd() const;
+
+ void setAlignment(Qt::Alignment flags);
+ Qt::Alignment alignment() const;
+
+ void setEdited(bool);
+ bool edited() const;
+
+ bool hasMarkedText() const;
+ QString markedText() const;
+
+ void cursorWordForward(bool mark);
+ void cursorWordBackward(bool mark);
+
+ // noops
+ bool autoUpdate() const { return true; }
+ virtual void setAutoUpdate(bool) {}
+
+ int totalWidth() const { return contentsWidth(); }
+ int totalHeight() const { return contentsHeight(); }
+
+ int maxLines() const { return QWIDGETSIZE_MAX; }
+ void setMaxLines(int) {}
+
+public Q_SLOTS:
+ void deselect() { selectAll(false); }
+
+protected:
+ QPoint cursorPoint() const;
+ virtual void insertAndMark(const QString&, bool mark);
+ virtual void newLine();
+ virtual void killLine();
+ virtual void pageUp(bool mark=false);
+ virtual void pageDown(bool mark=false);
+ virtual void cursorLeft(bool mark=false, bool wrap = true);
+ virtual void cursorRight(bool mark=false, bool wrap = true);
+ virtual void cursorUp(bool mark=false);
+ virtual void cursorDown(bool mark=false);
+ virtual void backspace();
+ virtual void home(bool mark=false);
+ virtual void end(bool mark=false);
+
+ bool getMarkedRegion(int *line1, int *col1, int *line2, int *col2) const;
+ int lineLength(int row) const;
+
+private:
+ Q_DISABLE_COPY(Q3MultiLineEdit)
+
+ Q3MultiLineEditData *d;
+};
+
+#endif // QT_NO_MULTILINEEDIT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3MULTILINEEDIT_H
diff --git a/src/qt3support/text/q3richtext.cpp b/src/qt3support/text/q3richtext.cpp
new file mode 100644
index 0000000..668a322
--- /dev/null
+++ b/src/qt3support/text/q3richtext.cpp
@@ -0,0 +1,8353 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3richtext_p.h"
+
+#ifndef QT_NO_RICHTEXT
+
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "q3cleanuphandler.h"
+#include "qcursor.h"
+#include "qdatastream.h"
+#include "q3dragobject.h"
+#include "qdrawutil.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qfont.h"
+#include "qimage.h"
+#include "qmap.h"
+#include "qmime.h"
+#include "q3paintdevicemetrics.h"
+#include "qpainter.h"
+#include "qstringlist.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "q3stylesheet.h"
+#include "qtextstream.h"
+#include <private/qtextengine_p.h>
+
+#include <stdlib.h>
+
+#if defined(Q_WS_X11)
+#include "qx11info_x11.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static Q3TextCursor* richTextExportStart = 0;
+static Q3TextCursor* richTextExportEnd = 0;
+
+class Q3TextFormatCollection;
+
+const int border_tolerance = 2;
+
+#ifdef Q_WS_WIN
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qt_windows.h"
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+static inline bool is_printer(QPainter *p)
+{
+ if (!p || !p->device())
+ return false;
+ return p->device()->devType() == QInternal::Printer;
+}
+
+static inline int scale(int value, QPainter *painter)
+{
+ if (is_printer(painter)) {
+ Q3PaintDeviceMetrics metrics(painter->device());
+#if defined(Q_WS_X11)
+ value = value * metrics.logicalDpiY() /
+ QX11Info::appDpiY(painter->device()->x11Screen());
+#elif defined (Q_WS_WIN)
+ HDC hdc = GetDC(0);
+ int gdc = GetDeviceCaps(hdc, LOGPIXELSY);
+ if (gdc)
+ value = value * metrics.logicalDpiY() / gdc;
+ ReleaseDC(0, hdc);
+#elif defined (Q_WS_MAC)
+ value = value * metrics.logicalDpiY() / 75; // ##### FIXME
+#elif defined (Q_WS_QWS)
+ value = value * metrics.logicalDpiY() / 75;
+#endif
+ }
+ return value;
+}
+
+
+static inline bool isBreakable(Q3TextString *string, int pos)
+{
+ if (string->at(pos).nobreak)
+ return false;
+ return (pos < string->length()-1 && string->at(pos+1).softBreak);
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void Q3TextCommandHistory::addCommand(Q3TextCommand *cmd)
+{
+ if (current < history.count() - 1) {
+ QList<Q3TextCommand *> commands;
+
+ for (int i = 0; i <= current; ++i)
+ commands.insert(i, history.takeFirst());
+
+ commands.append(cmd);
+ while (!history.isEmpty())
+ delete history.takeFirst();
+ history = commands;
+ } else {
+ history.append(cmd);
+ }
+
+ if (history.count() > steps)
+ delete history.takeFirst();
+ else
+ ++current;
+}
+
+Q3TextCursor *Q3TextCommandHistory::undo(Q3TextCursor *c)
+{
+ if (current > -1) {
+ Q3TextCursor *c2 = history.at(current)->unexecute(c);
+ --current;
+ return c2;
+ }
+ return 0;
+}
+
+Q3TextCursor *Q3TextCommandHistory::redo(Q3TextCursor *c)
+{
+ if (current > -1) {
+ if (current < history.count() - 1) {
+ ++current;
+ return history.at(current)->execute(c);
+ }
+ } else {
+ if (history.count() > 0) {
+ ++current;
+ return history.at(current)->execute(c);
+ }
+ }
+ return 0;
+}
+
+bool Q3TextCommandHistory::isUndoAvailable()
+{
+ return current > -1;
+}
+
+bool Q3TextCommandHistory::isRedoAvailable()
+{
+ return (current > -1 && current < history.count() - 1) || (current == -1 && history.count() > 0);
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
+ const QByteArray& oldStyleInfo)
+ : Q3TextCommand(dc), id(i), index(idx), parag(0), text(str), styleInformation(oldStyleInfo)
+{
+ for (int j = 0; j < (int)text.size(); ++j) {
+ if (text[j].format())
+ text[j].format()->addRef();
+ }
+}
+
+Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str)
+ : Q3TextCommand(0), id(-1), index(idx), parag(p), text(str)
+{
+ for (int i = 0; i < (int)text.size(); ++i) {
+ if (text[i].format())
+ text[i].format()->addRef();
+ }
+}
+
+Q3TextDeleteCommand::~Q3TextDeleteCommand()
+{
+ for (int i = 0; i < (int)text.size(); ++i) {
+ if (text[i].format())
+ text[i].format()->removeRef();
+ }
+ text.resize(0);
+}
+
+Q3TextCursor *Q3TextDeleteCommand::execute(Q3TextCursor *c)
+{
+ Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
+ if (!s) {
+ qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
+ return 0;
+ }
+
+ cursor.setParagraph(s);
+ cursor.setIndex(index);
+ int len = text.size();
+ if (c)
+ *c = cursor;
+ if (doc) {
+ doc->setSelectionStart(Q3TextDocument::Temp, cursor);
+ for (int i = 0; i < len; ++i)
+ cursor.gotoNextLetter();
+ doc->setSelectionEnd(Q3TextDocument::Temp, cursor);
+ doc->removeSelectedText(Q3TextDocument::Temp, &cursor);
+ if (c)
+ *c = cursor;
+ } else {
+ s->remove(index, len);
+ }
+
+ return c;
+}
+
+Q3TextCursor *Q3TextDeleteCommand::unexecute(Q3TextCursor *c)
+{
+ Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
+ if (!s) {
+ qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
+ return 0;
+ }
+
+ cursor.setParagraph(s);
+ cursor.setIndex(index);
+ QString str = Q3TextString::toString(text);
+ cursor.insert(str, true, &text);
+ if (c)
+ *c = cursor;
+ cursor.setParagraph(s);
+ cursor.setIndex(index);
+
+#ifndef QT_NO_DATASTREAM
+ if (!styleInformation.isEmpty()) {
+ QDataStream styleStream(&styleInformation, IO_ReadOnly);
+ int num;
+ styleStream >> num;
+ Q3TextParagraph *p = s;
+ while (num-- && p) {
+ p->readStyleInformation(styleStream);
+ p = p->next();
+ }
+ }
+#endif
+ s = cursor.paragraph();
+ while (s) {
+ s->format();
+ s->setChanged(true);
+ if (s == c->paragraph())
+ break;
+ s = s->next();
+ }
+
+ return &cursor;
+}
+
+Q3TextFormatCommand::Q3TextFormatCommand(Q3TextDocument *dc, int sid, int sidx, int eid, int eidx,
+ const QVector<Q3TextStringChar> &old, Q3TextFormat *f, int fl)
+ : Q3TextCommand(dc), startId(sid), startIndex(sidx), endId(eid), endIndex(eidx), format(f), oldFormats(old), flags(fl)
+{
+ format = dc->formatCollection()->format(f);
+ for (int j = 0; j < (int)oldFormats.size(); ++j) {
+ if (oldFormats[j].format())
+ oldFormats[j].format()->addRef();
+ }
+}
+
+Q3TextFormatCommand::~Q3TextFormatCommand()
+{
+ format->removeRef();
+ for (int j = 0; j < (int)oldFormats.size(); ++j) {
+ if (oldFormats[j].format())
+ oldFormats[j].format()->removeRef();
+ }
+}
+
+Q3TextCursor *Q3TextFormatCommand::execute(Q3TextCursor *c)
+{
+ Q3TextParagraph *sp = doc->paragAt(startId);
+ Q3TextParagraph *ep = doc->paragAt(endId);
+ if (!sp || !ep)
+ return c;
+
+ Q3TextCursor start(doc);
+ start.setParagraph(sp);
+ start.setIndex(startIndex);
+ Q3TextCursor end(doc);
+ end.setParagraph(ep);
+ end.setIndex(endIndex);
+
+ doc->setSelectionStart(Q3TextDocument::Temp, start);
+ doc->setSelectionEnd(Q3TextDocument::Temp, end);
+ doc->setFormat(Q3TextDocument::Temp, format, flags);
+ doc->removeSelection(Q3TextDocument::Temp);
+ if (endIndex == ep->length())
+ end.gotoLeft();
+ *c = end;
+ return c;
+}
+
+Q3TextCursor *Q3TextFormatCommand::unexecute(Q3TextCursor *c)
+{
+ Q3TextParagraph *sp = doc->paragAt(startId);
+ Q3TextParagraph *ep = doc->paragAt(endId);
+ if (!sp || !ep)
+ return 0;
+
+ int idx = startIndex;
+ int fIndex = 0;
+ while ( fIndex < int(oldFormats.size()) ) {
+ if (oldFormats.at(fIndex).c == QLatin1Char('\n')) {
+ if (idx > 0) {
+ if (idx < sp->length() && fIndex > 0)
+ sp->setFormat(idx, 1, oldFormats.at(fIndex - 1).format());
+ if (sp == ep)
+ break;
+ sp = sp->next();
+ idx = 0;
+ }
+ fIndex++;
+ }
+ if (oldFormats.at(fIndex).format())
+ sp->setFormat(idx, 1, oldFormats.at(fIndex).format());
+ idx++;
+ fIndex++;
+ if (fIndex >= (int)oldFormats.size())
+ break;
+ if (idx >= sp->length()) {
+ if (sp == ep)
+ break;
+ sp = sp->next();
+ idx = 0;
+ }
+ }
+
+ Q3TextCursor end(doc);
+ end.setParagraph(ep);
+ end.setIndex(endIndex);
+ if (endIndex == ep->length())
+ end.gotoLeft();
+ *c = end;
+ return c;
+}
+
+Q3TextStyleCommand::Q3TextStyleCommand(Q3TextDocument *dc, int fParag, int lParag, const QByteArray& beforeChange)
+ : Q3TextCommand(dc), firstParag(fParag), lastParag(lParag), before(beforeChange)
+{
+ after = readStyleInformation( dc, fParag, lParag);
+}
+
+
+QByteArray Q3TextStyleCommand::readStyleInformation( Q3TextDocument* doc, int fParag, int lParag)
+{
+ QByteArray style;
+#ifndef QT_NO_DATASTREAM
+ Q3TextParagraph *p = doc->paragAt(fParag);
+ if (!p)
+ return style;
+ QDataStream styleStream(&style, IO_WriteOnly);
+ int num = lParag - fParag + 1;
+ styleStream << num;
+ while (num -- && p) {
+ p->writeStyleInformation(styleStream);
+ p = p->next();
+ }
+#endif
+ return style;
+}
+
+void Q3TextStyleCommand::writeStyleInformation( Q3TextDocument* doc, int fParag, const QByteArray& style)
+{
+#ifndef QT_NO_DATASTREAM
+ Q3TextParagraph *p = doc->paragAt(fParag);
+ if (!p)
+ return;
+ QByteArray copy = style;
+ QDataStream styleStream(&copy, IO_ReadOnly);
+ int num;
+ styleStream >> num;
+ while (num-- && p) {
+ p->readStyleInformation(styleStream);
+ p = p->next();
+ }
+#endif
+}
+
+Q3TextCursor *Q3TextStyleCommand::execute(Q3TextCursor *c)
+{
+ writeStyleInformation(doc, firstParag, after);
+ return c;
+}
+
+Q3TextCursor *Q3TextStyleCommand::unexecute(Q3TextCursor *c)
+{
+ writeStyleInformation(doc, firstParag, before);
+ return c;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextCursor::Q3TextCursor(Q3TextDocument *dc)
+ : idx(0), tmpX(-1), ox(0), oy(0),
+ valid(true)
+{
+ para = dc ? dc->firstParagraph() : 0;
+}
+
+Q3TextCursor::Q3TextCursor(const Q3TextCursor &c)
+{
+ ox = c.ox;
+ oy = c.oy;
+ idx = c.idx;
+ para = c.para;
+ tmpX = c.tmpX;
+ indices = c.indices;
+ paras = c.paras;
+ xOffsets = c.xOffsets;
+ yOffsets = c.yOffsets;
+ valid = c.valid;
+}
+
+Q3TextCursor::~Q3TextCursor()
+{
+}
+
+Q3TextCursor &Q3TextCursor::operator=(const Q3TextCursor &c)
+{
+ ox = c.ox;
+ oy = c.oy;
+ idx = c.idx;
+ para = c.para;
+ tmpX = c.tmpX;
+ indices = c.indices;
+ paras = c.paras;
+ xOffsets = c.xOffsets;
+ yOffsets = c.yOffsets;
+ valid = c.valid;
+
+ return *this;
+}
+
+bool Q3TextCursor::operator==(const Q3TextCursor &c) const
+{
+ return para == c.para && idx == c.idx;
+}
+
+int Q3TextCursor::totalOffsetX() const
+{
+ int xoff = ox;
+ for (QStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit)
+ xoff += *xit;
+ return xoff;
+}
+
+int Q3TextCursor::totalOffsetY() const
+{
+ int yoff = oy;
+ for (QStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit)
+ yoff += *yit;
+ return yoff;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextCursor::gotoIntoNested(const QPoint &globalPos)
+{
+ if (!para)
+ return;
+ Q_ASSERT(para->at(idx)->isCustom());
+ push();
+ ox = 0;
+ int bl, y;
+ para->lineHeightOfChar(idx, &bl, &y);
+ oy = y + para->rect().y();
+ ox = para->at(idx)->x;
+ Q3TextDocument* doc = document();
+ para->at(idx)->customItem()->enterAt(this, doc, para, idx, ox, oy, globalPos-QPoint(ox,oy));
+}
+#endif
+
+void Q3TextCursor::invalidateNested()
+{
+ if (nestedDepth()) {
+ QStack<Q3TextParagraph*>::Iterator it = paras.begin();
+ QStack<int>::Iterator it2 = indices.begin();
+ for (; it != paras.end(); ++it, ++it2) {
+ if (*it == para)
+ continue;
+ (*it)->invalidate(0);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if ((*it)->at(*it2)->isCustom())
+ (*it)->at(*it2)->customItem()->invalidate();
+#endif
+ }
+ }
+}
+
+void Q3TextCursor::insert(const QString &str, bool checkNewLine, QVector<Q3TextStringChar> *formatting)
+{
+ tmpX = -1;
+ bool justInsert = true;
+ QString s(str);
+#if defined(Q_WS_WIN)
+ if (checkNewLine) {
+ int i = 0;
+ while ((i = s.indexOf(QLatin1Char('\r'), i)) != -1)
+ s.remove(i ,1);
+ }
+#endif
+ if (checkNewLine)
+ justInsert = s.indexOf(QLatin1Char('\n')) == -1;
+ if (justInsert) { // we ignore new lines and insert all in the current para at the current index
+ para->insert(idx, s.unicode(), s.length());
+ if (formatting) {
+ for (int i = 0; i < (int)s.length(); ++i) {
+ if (formatting->at(i).format()) {
+ formatting->at(i).format()->addRef();
+ para->string()->setFormat(idx + i, formatting->at(i).format(), true);
+ }
+ }
+ }
+ idx += s.length();
+ } else { // we split at new lines
+ int start = -1;
+ int end;
+ int y = para->rect().y() + para->rect().height();
+ int lastIndex = 0;
+ do {
+ end = s.indexOf(QLatin1Char('\n'), start + 1); // find line break
+ if (end == -1) // didn't find one, so end of line is end of string
+ end = s.length();
+ int len = (start == -1 ? end : end - start - 1);
+ if (len > 0) // insert the line
+ para->insert(idx, s.unicode() + start + 1, len);
+ else
+ para->invalidate(0);
+ if (formatting) { // set formats to the chars of the line
+ for (int i = 0; i < len; ++i) {
+ if (formatting->at(i + lastIndex).format()) {
+ formatting->at(i + lastIndex).format()->addRef();
+ para->string()->setFormat(i + idx, formatting->at(i + lastIndex).format(), true);
+ }
+ }
+ lastIndex += len;
+ }
+ start = end; // next start is at the end of this line
+ idx += len; // increase the index of the cursor to the end of the inserted text
+ if (s[end] == QLatin1Char('\n')) { // if at the end was a line break, break the line
+ splitAndInsertEmptyParagraph(false, true);
+ para->setEndState(-1);
+ para->prev()->format(-1, false);
+ lastIndex++;
+ }
+
+ } while (end < (int)s.length());
+
+ para->format(-1, false);
+ int dy = para->rect().y() + para->rect().height() - y;
+ Q3TextParagraph *p = para;
+ p->setParagId(p->prev() ? p->prev()->paragId() + 1 : 0);
+ p = p->next();
+ while (p) {
+ p->setParagId(p->prev()->paragId() + 1);
+ p->move(dy);
+ p->invalidate(0);
+ p->setEndState(-1);
+ p = p->next();
+ }
+ }
+
+ int h = para->rect().height();
+ para->format(-1, true);
+ if (h != para->rect().height())
+ invalidateNested();
+ else if (para->document() && para->document()->parent())
+ para->document()->nextDoubleBuffered = true;
+
+ fixCursorPosition();
+}
+
+void Q3TextCursor::gotoLeft()
+{
+ if (para->string()->isRightToLeft())
+ gotoNextLetter();
+ else
+ gotoPreviousLetter();
+}
+
+void Q3TextCursor::gotoPreviousLetter()
+{
+ tmpX = -1;
+
+ if (idx > 0) {
+ idx = para->string()->previousCursorPosition(idx);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ const Q3TextStringChar *tsc = para->at(idx);
+ if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
+ processNesting(EnterEnd);
+#endif
+ } else if (para->prev()) {
+ para = para->prev();
+ while (!para->isVisible() && para->prev())
+ para = para->prev();
+ idx = para->length() - 1;
+ } else if (nestedDepth()) {
+ pop();
+ processNesting(Prev);
+ if (idx == -1) {
+ pop();
+ if (idx > 0) {
+ idx = para->string()->previousCursorPosition(idx);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ const Q3TextStringChar *tsc = para->at(idx);
+ if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
+ processNesting(EnterEnd);
+#endif
+ } else if (para->prev()) {
+ para = para->prev();
+ idx = para->length() - 1;
+ }
+ }
+ }
+}
+
+void Q3TextCursor::push()
+{
+ indices.push(idx);
+ paras.push(para);
+ xOffsets.push(ox);
+ yOffsets.push(oy);
+}
+
+void Q3TextCursor::pop()
+{
+ if (indices.isEmpty())
+ return;
+ idx = indices.pop();
+ para = paras.pop();
+ ox = xOffsets.pop();
+ oy = yOffsets.pop();
+}
+
+void Q3TextCursor::restoreState()
+{
+ while (!indices.isEmpty())
+ pop();
+}
+
+bool Q3TextCursor::place(const QPoint &p, Q3TextParagraph *s, bool link)
+{
+ QPoint pos(p);
+ QRect r;
+ Q3TextParagraph *str = s;
+ if (pos.y() < s->rect().y()) {
+ pos.setY(s->rect().y());
+#ifdef Q_WS_MAC
+ pos.setX(s->rect().x());
+#endif
+ }
+ while (s) {
+ r = s->rect();
+ r.setWidth(document() ? document()->width() : QWIDGETSIZE_MAX);
+ if (s->isVisible())
+ str = s;
+ if (pos.y() >= r.y() && pos.y() <= r.y() + r.height())
+ break;
+ if (!s->next()) {
+#ifdef Q_WS_MAC
+ pos.setX(s->rect().x() + s->rect().width());
+#endif
+ break;
+ }
+ s = s->next();
+ }
+
+ if (!s || !str)
+ return false;
+
+ s = str;
+
+ setParagraph(s);
+ int y = s->rect().y();
+ int lines = s->lines();
+ Q3TextStringChar *chr = 0;
+ int index = 0;
+ int i = 0;
+ int cy = 0;
+ int ch = 0;
+ for (; i < lines; ++i) {
+ chr = s->lineStartOfLine(i, &index);
+ cy = s->lineY(i);
+ ch = s->lineHeight(i);
+ if (!chr)
+ return false;
+ if (pos.y() <= y + cy + ch)
+ break;
+ }
+ int nextLine;
+ if (i < lines - 1)
+ s->lineStartOfLine(i+1, &nextLine);
+ else
+ nextLine = s->length();
+ i = index;
+ int x = s->rect().x();
+ if (pos.x() < x)
+ pos.setX(x + 1);
+ int cw;
+ int curpos = s->length()-1;
+ int dist = 10000000;
+ bool inCustom = false;
+ while (i < nextLine) {
+ chr = s->at(i);
+ int cpos = x + chr->x;
+ cw = s->string()->width(i);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (chr->isCustom() && chr->customItem()->isNested()) {
+ if (pos.x() >= cpos && pos.x() <= cpos + cw &&
+ pos.y() >= y + cy && pos.y() <= y + cy + chr->height()) {
+ inCustom = true;
+ curpos = i;
+ break;
+ }
+ } else
+#endif
+ {
+ if(chr->rightToLeft)
+ cpos += cw;
+ int diff = cpos - pos.x();
+ bool dm = diff < 0 ? !chr->rightToLeft : chr->rightToLeft;
+ if ((QABS(diff) < dist || (dist == diff && dm == true)) && para->string()->validCursorPosition(i)) {
+ dist = QABS(diff);
+ if (!link || pos.x() >= x + chr->x)
+ curpos = i;
+ }
+ }
+ i++;
+ }
+ setIndex(curpos);
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (inCustom && para->document() && para->at(curpos)->isCustom() && para->at(curpos)->customItem()->isNested()) {
+ Q3TextDocument *oldDoc = para->document();
+ gotoIntoNested(pos);
+ if (oldDoc == para->document())
+ return true;
+ QPoint p(pos.x() - offsetX(), pos.y() - offsetY());
+ if (!place(p, document()->firstParagraph(), link))
+ pop();
+ }
+#endif
+ return true;
+}
+
+bool Q3TextCursor::processNesting(Operation op)
+{
+ if (!para->document())
+ return false;
+ Q3TextDocument* doc = para->document();
+ push();
+ ox = para->at(idx)->x;
+ int bl, y;
+ para->lineHeightOfChar(idx, &bl, &y);
+ oy = y + para->rect().y();
+ bool ok = false;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ switch (op) {
+ case EnterBegin:
+ ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy);
+ break;
+ case EnterEnd:
+ ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy, true);
+ break;
+ case Next:
+ ok = para->at(idx)->customItem()->next(this, doc, para, idx, ox, oy);
+ break;
+ case Prev:
+ ok = para->at(idx)->customItem()->prev(this, doc, para, idx, ox, oy);
+ break;
+ case Down:
+ ok = para->at(idx)->customItem()->down(this, doc, para, idx, ox, oy);
+ break;
+ case Up:
+ ok = para->at(idx)->customItem()->up(this, doc, para, idx, ox, oy);
+ break;
+ }
+ if (!ok)
+#endif
+ pop();
+ return ok;
+}
+
+void Q3TextCursor::gotoRight()
+{
+ if (para->string()->isRightToLeft())
+ gotoPreviousLetter();
+ else
+ gotoNextLetter();
+}
+
+void Q3TextCursor::gotoNextLetter()
+{
+ tmpX = -1;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ const Q3TextStringChar *tsc = para->at(idx);
+ if (tsc && tsc->isCustom() && tsc->customItem()->isNested()) {
+ if (processNesting(EnterBegin))
+ return;
+ }
+#endif
+
+ if (idx < para->length() - 1) {
+ idx = para->string()->nextCursorPosition(idx);
+ } else if (para->next()) {
+ para = para->next();
+ while (!para->isVisible() && para->next())
+ para = para->next();
+ idx = 0;
+ } else if (nestedDepth()) {
+ pop();
+ processNesting(Next);
+ if (idx == -1) {
+ pop();
+ if (idx < para->length() - 1) {
+ idx = para->string()->nextCursorPosition(idx);
+ } else if (para->next()) {
+ para = para->next();
+ idx = 0;
+ }
+ }
+ }
+}
+
+void Q3TextCursor::gotoUp()
+{
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ if (tmpX < 0)
+ tmpX = x();
+
+ if (indexOfLineStart == 0) {
+ if (!para->prev()) {
+ if (!nestedDepth())
+ return;
+ pop();
+ processNesting(Up);
+ if (idx == -1) {
+ pop();
+ if (!para->prev())
+ return;
+ idx = tmpX = 0;
+ } else {
+ tmpX = -1;
+ return;
+ }
+ }
+ Q3TextParagraph *p = para->prev();
+ while (p && !p->isVisible())
+ p = p->prev();
+ if (p)
+ para = p;
+ int lastLine = para->lines() - 1;
+ if (!para->lineStartOfLine(lastLine, &indexOfLineStart))
+ return;
+ idx = indexOfLineStart;
+ while (idx < para->length()-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ } else {
+ --line;
+ int oldIndexOfLineStart = indexOfLineStart;
+ if (!para->lineStartOfLine(line, &indexOfLineStart))
+ return;
+ idx = indexOfLineStart;
+ while (idx < oldIndexOfLineStart-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ }
+ fixCursorPosition();
+}
+
+void Q3TextCursor::gotoDown()
+{
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ if (tmpX < 0)
+ tmpX = x();
+
+ if (line == para->lines() - 1) {
+ if (!para->next()) {
+ if (!nestedDepth())
+ return;
+ pop();
+ processNesting(Down);
+ if (idx == -1) {
+ pop();
+ if (!para->next())
+ return;
+ idx = tmpX = 0;
+ } else {
+ tmpX = -1;
+ return;
+ }
+ }
+ Q3TextParagraph *s = para->next();
+ while (s && !s->isVisible())
+ s = s->next();
+ if (s)
+ para = s;
+ if (!para->lineStartOfLine(0, &indexOfLineStart))
+ return;
+ int end;
+ if (para->lines() == 1)
+ end = para->length();
+ else
+ para->lineStartOfLine(1, &end);
+
+ idx = indexOfLineStart;
+ while (idx < end-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ } else {
+ ++line;
+ int end;
+ if (line == para->lines() - 1)
+ end = para->length();
+ else
+ para->lineStartOfLine(line + 1, &end);
+ if (!para->lineStartOfLine(line, &indexOfLineStart))
+ return;
+ idx = indexOfLineStart;
+ while (idx < end-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ }
+ fixCursorPosition();
+}
+
+void Q3TextCursor::gotoLineEnd()
+{
+ tmpX = -1;
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ if (line == para->lines() - 1) {
+ idx = para->length() - 1;
+ } else {
+ c = para->lineStartOfLine(++line, &indexOfLineStart);
+ indexOfLineStart--;
+ idx = indexOfLineStart;
+ }
+}
+
+void Q3TextCursor::gotoLineStart()
+{
+ tmpX = -1;
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ idx = indexOfLineStart;
+}
+
+void Q3TextCursor::gotoHome()
+{
+ if (topParagraph()->document())
+ gotoPosition(topParagraph()->document()->firstParagraph());
+ else
+ gotoLineStart();
+}
+
+void Q3TextCursor::gotoEnd()
+{
+ if (topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid())
+ gotoPosition(topParagraph()->document()->lastParagraph(),
+ topParagraph()->document()->lastParagraph()->length() - 1);
+ else
+ gotoLineEnd();
+}
+
+void Q3TextCursor::gotoPageUp(int visibleHeight)
+{
+ int targetY = globalY() - visibleHeight;
+ Q3TextParagraph* old; int index;
+ do {
+ old = para; index = idx;
+ gotoUp();
+ } while ((old != para || index != idx) && globalY() > targetY);
+}
+
+void Q3TextCursor::gotoPageDown(int visibleHeight)
+{
+ int targetY = globalY() + visibleHeight;
+ Q3TextParagraph* old; int index;
+ do {
+ old = para; index = idx;
+ gotoDown();
+ } while ((old != para || index != idx) && globalY() < targetY);
+}
+
+void Q3TextCursor::gotoWordRight()
+{
+ if (para->string()->isRightToLeft())
+ gotoPreviousWord();
+ else
+ gotoNextWord();
+}
+
+void Q3TextCursor::gotoWordLeft()
+{
+ if (para->string()->isRightToLeft())
+ gotoNextWord();
+ else
+ gotoPreviousWord();
+}
+
+static bool is_seperator(const QChar &c, bool onlySpace)
+{
+ if (onlySpace)
+ return c.isSpace();
+ return c.isSpace() ||
+ c == QLatin1Char('\t') ||
+ c == QLatin1Char('.') ||
+ c == QLatin1Char(',') ||
+ c == QLatin1Char(':') ||
+ c == QLatin1Char(';') ||
+ c == QLatin1Char('-') ||
+ c == QLatin1Char('<') ||
+ c == QLatin1Char('>') ||
+ c == QLatin1Char('[') ||
+ c == QLatin1Char(']') ||
+ c == QLatin1Char('(') ||
+ c == QLatin1Char(')') ||
+ c == QLatin1Char('{') ||
+ c == QLatin1Char('}');
+}
+
+void Q3TextCursor::gotoPreviousWord(bool onlySpace)
+{
+ gotoPreviousLetter();
+ tmpX = -1;
+ Q3TextString *s = para->string();
+ bool allowSame = false;
+ if (idx == ((int)s->length()-1))
+ return;
+ for (int i = idx; i >= 0; --i) {
+ if (is_seperator(s->at(i).c, onlySpace)) {
+ if (!allowSame)
+ continue;
+ idx = i + 1;
+ return;
+ }
+ if (!allowSame && !is_seperator(s->at(i).c, onlySpace))
+ allowSame = true;
+ }
+ idx = 0;
+}
+
+void Q3TextCursor::gotoNextWord(bool onlySpace)
+{
+ tmpX = -1;
+ Q3TextString *s = para->string();
+ bool allowSame = false;
+ for (int i = idx; i < (int)s->length(); ++i) {
+ if (!is_seperator(s->at(i).c, onlySpace)) {
+ if (!allowSame)
+ continue;
+ idx = i;
+ return;
+ }
+ if (!allowSame && is_seperator(s->at(i).c, onlySpace))
+ allowSame = true;
+
+ }
+
+ if (idx < ((int)s->length()-1)) {
+ gotoLineEnd();
+ } else if (para->next()) {
+ Q3TextParagraph *p = para->next();
+ while (p && !p->isVisible())
+ p = p->next();
+ if (s) {
+ para = p;
+ idx = 0;
+ }
+ } else {
+ gotoLineEnd();
+ }
+}
+
+bool Q3TextCursor::atParagStart()
+{
+ return idx == 0;
+}
+
+bool Q3TextCursor::atParagEnd()
+{
+ return idx == para->length() - 1;
+}
+
+void Q3TextCursor::splitAndInsertEmptyParagraph(bool ind, bool updateIds)
+{
+ if (!para->document())
+ return;
+ tmpX = -1;
+ Q3TextFormat *f = 0;
+ if (para->document()->useFormatCollection()) {
+ f = para->at(idx)->format();
+ if (idx == para->length() - 1 && idx > 0)
+ f = para->at(idx - 1)->format();
+ if (f->isMisspelled()) {
+ f->removeRef();
+ f = para->document()->formatCollection()->format(f->font(), f->color());
+ }
+ }
+
+ if (atParagEnd()) {
+ Q3TextParagraph *n = para->next();
+ Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
+ if (f)
+ s->setFormat(0, 1, f, true);
+ s->copyParagData(para);
+ if (ind) {
+ int oi, ni;
+ s->indent(&oi, &ni);
+ para = s;
+ idx = ni;
+ } else {
+ para = s;
+ idx = 0;
+ }
+ } else if (atParagStart()) {
+ Q3TextParagraph *p = para->prev();
+ Q3TextParagraph *s = para->document()->createParagraph(para->document(), p, para, updateIds);
+ if (f)
+ s->setFormat(0, 1, f, true);
+ s->copyParagData(para);
+ if (ind) {
+ s->indent();
+ s->format();
+ indent();
+ para->format();
+ }
+ } else {
+ QString str = para->string()->toString().mid(idx, 0xFFFFFF);
+ Q3TextParagraph *n = para->next();
+ Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
+ s->copyParagData(para);
+ s->remove(0, 1);
+ s->append(str, true);
+ for (int i = 0; i < str.length(); ++i) {
+ Q3TextStringChar* tsc = para->at(idx + i);
+ s->setFormat(i, 1, tsc->format(), true);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (tsc->isCustom()) {
+ Q3TextCustomItem * item = tsc->customItem();
+ s->at(i)->setCustomItem(item);
+ tsc->loseCustomItem();
+ }
+#endif
+ if (tsc->isAnchor())
+ s->at(i)->setAnchor(tsc->anchorName(),
+ tsc->anchorHref());
+ }
+ para->truncate(idx);
+ if (ind) {
+ int oi, ni;
+ s->indent(&oi, &ni);
+ para = s;
+ idx = ni;
+ } else {
+ para = s;
+ idx = 0;
+ }
+ }
+
+ invalidateNested();
+}
+
+bool Q3TextCursor::remove()
+{
+ tmpX = -1;
+ if (!atParagEnd()) {
+ int next = para->string()->nextCursorPosition(idx);
+ para->remove(idx, next-idx);
+ int h = para->rect().height();
+ para->format(-1, true);
+ if (h != para->rect().height())
+ invalidateNested();
+ else if (para->document() && para->document()->parent())
+ para->document()->nextDoubleBuffered = true;
+ return false;
+ } else if (para->next()) {
+ para->join(para->next());
+ invalidateNested();
+ return true;
+ }
+ return false;
+}
+
+/* needed to implement backspace the correct way */
+bool Q3TextCursor::removePreviousChar()
+{
+ tmpX = -1;
+ if (!atParagStart()) {
+ para->remove(idx-1, 1);
+ int h = para->rect().height();
+ idx--;
+ // shouldn't be needed, just to make sure.
+ fixCursorPosition();
+ para->format(-1, true);
+ if (h != para->rect().height())
+ invalidateNested();
+ else if (para->document() && para->document()->parent())
+ para->document()->nextDoubleBuffered = true;
+ return false;
+ } else if (para->prev()) {
+ para = para->prev();
+ para->join(para->next());
+ invalidateNested();
+ return true;
+ }
+ return false;
+}
+
+void Q3TextCursor::indent()
+{
+ int oi = 0, ni = 0;
+ para->indent(&oi, &ni);
+ if (oi == ni)
+ return;
+
+ if (idx >= oi)
+ idx += ni - oi;
+ else
+ idx = ni;
+}
+
+void Q3TextCursor::fixCursorPosition()
+{
+ // searches for the closest valid cursor position
+ if (para->string()->validCursorPosition(idx))
+ return;
+
+ int lineIdx;
+ Q3TextStringChar *start = para->lineStartOfChar(idx, &lineIdx, 0);
+ int x = para->string()->at(idx).x;
+ int diff = QABS(start->x - x);
+ int best = lineIdx;
+
+ Q3TextStringChar *c = start;
+ ++c;
+
+ Q3TextStringChar *end = &para->string()->at(para->length()-1);
+ while (c <= end && !c->lineStart) {
+ int xp = c->x;
+ if (c->rightToLeft)
+ xp += para->string()->width(lineIdx + (c-start));
+ int ndiff = QABS(xp - x);
+ if (ndiff < diff && para->string()->validCursorPosition(lineIdx + (c-start))) {
+ diff = ndiff;
+ best = lineIdx + (c-start);
+ }
+ ++c;
+ }
+ idx = best;
+}
+
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextDocument::Q3TextDocument(Q3TextDocument *p)
+ : par(p), parentPar(0)
+#ifndef QT_NO_TEXTCUSTOMITEM
+ , tc(0)
+#endif
+ , tArray(0), tStopWidth(0)
+{
+ fCollection = par ? par->fCollection : new Q3TextFormatCollection;
+ init();
+}
+
+void Q3TextDocument::init()
+{
+ oTextValid = true;
+ mightHaveCustomItems = false;
+ if (par)
+ par->insertChild(this);
+ pProcessor = 0;
+ useFC = true;
+ pFormatter = 0;
+ indenter = 0;
+ fParag = 0;
+ txtFormat = Qt::AutoText;
+ preferRichText = false;
+ pages = false;
+ focusIndicator.parag = 0;
+ minw = 0;
+ wused = 0;
+ minwParag = curParag = 0;
+ align = Qt::AlignAuto;
+ nSelections = 1;
+
+ setStyleSheet(Q3StyleSheet::defaultSheet());
+#ifndef QT_NO_MIME
+ factory_ = Q3MimeSourceFactory::defaultFactory();
+#endif
+ contxt.clear();
+
+ underlLinks = par ? par->underlLinks : true;
+ backBrush = 0;
+ buf_pixmap = 0;
+ nextDoubleBuffered = false;
+
+ if (par)
+ withoutDoubleBuffer = par->withoutDoubleBuffer;
+ else
+ withoutDoubleBuffer = false;
+
+ lParag = fParag = createParagraph(this, 0, 0);
+
+ cx = 0;
+ cy = 2;
+ if (par)
+ cx = cy = 0;
+ cw = 600;
+ vw = 0;
+ flow_ = new Q3TextFlow;
+ flow_->setWidth(cw);
+
+ leftmargin = rightmargin = 4;
+ scaleFontsFactor = 1;
+
+ commandHistory = new Q3TextCommandHistory(100);
+ tStopWidth = formatCollection()->defaultFormat()->width(QLatin1Char('x')) * 8;
+}
+
+Q3TextDocument::~Q3TextDocument()
+{
+ delete commandHistory;
+ if (par)
+ par->removeChild(this);
+ clear();
+ delete flow_;
+ if (!par) {
+ delete pFormatter;
+ delete fCollection;
+ }
+ delete pProcessor;
+ delete buf_pixmap;
+ delete indenter;
+ delete backBrush;
+ delete [] tArray;
+}
+
+void Q3TextDocument::clear(bool createEmptyParag)
+{
+ while (fParag) {
+ Q3TextParagraph *p = fParag->next();
+ delete fParag;
+ fParag = p;
+ }
+ if (flow_)
+ flow_->clear();
+ fParag = lParag = 0;
+ if (createEmptyParag)
+ fParag = lParag = createParagraph(this);
+ selections.clear();
+ oText.clear();
+ oTextValid = false;
+}
+
+int Q3TextDocument::widthUsed() const
+{
+ return wused + 2*border_tolerance;
+}
+
+int Q3TextDocument::height() const
+{
+ int h = 0;
+ if (lParag)
+ h = lParag->rect().top() + lParag->rect().height() + 1;
+ int fh = flow_->boundingRect().bottom();
+ return qMax(h, fh);
+}
+
+
+
+Q3TextParagraph *Q3TextDocument::createParagraph(Q3TextDocument *dc, Q3TextParagraph *pr, Q3TextParagraph *nx, bool updateIds)
+{
+ return new Q3TextParagraph(dc, pr, nx, updateIds);
+}
+
+bool Q3TextDocument::setMinimumWidth(int needed, int used, Q3TextParagraph *p)
+{
+ if (needed == -1) {
+ minw = 0;
+ wused = 0;
+ p = 0;
+ }
+ if (p == minwParag) {
+ if (minw > needed) {
+ Q3TextParagraph *tp = fParag;
+ while (tp) {
+ if (tp != p && tp->minwidth > needed) {
+ needed = tp->minwidth;
+ minwParag = tp;
+ }
+ tp = tp->n;
+ }
+ }
+ minw = needed;
+ emit minimumWidthChanged(minw);
+ } else if (needed > minw) {
+ minw = needed;
+ minwParag = p;
+ emit minimumWidthChanged(minw);
+ }
+ wused = qMax(wused, used);
+ wused = qMax(wused, minw);
+ cw = qMax(minw, cw);
+ return true;
+}
+
+void Q3TextDocument::setPlainText(const QString &text)
+{
+ preferRichText = false;
+ clear();
+ oTextValid = true;
+ oText = text;
+
+ int lastNl = 0;
+ int nl = text.indexOf(QLatin1Char('\n'));
+ if (nl == -1) {
+ lParag = createParagraph(this, lParag, 0);
+ if (!fParag)
+ fParag = lParag;
+ QString s = text;
+ if (!s.isEmpty()) {
+ if (s[(int)s.length() - 1] == QLatin1Char('\r'))
+ s.remove(s.length() - 1, 1);
+ lParag->append(s);
+ }
+ } else {
+ for (;;) {
+ lParag = createParagraph(this, lParag, 0);
+ if (!fParag)
+ fParag = lParag;
+ int l = nl - lastNl;
+ if (l > 0) {
+ if (text.unicode()[nl-1] == QLatin1Char('\r'))
+ l--;
+ QString cs = QString::fromRawData(text.unicode()+lastNl, l);
+ lParag->append(cs);
+ }
+ if (nl == (int)text.length())
+ break;
+ lastNl = nl + 1;
+ nl = text.indexOf(QLatin1Char('\n'), nl + 1);
+ if (nl == -1)
+ nl = text.length();
+ }
+ }
+ if (!lParag)
+ lParag = fParag = createParagraph(this, 0, 0);
+}
+
+struct Q3TextDocumentTag {
+ Q3TextDocumentTag(){}
+ Q3TextDocumentTag(const QString&n, const Q3StyleSheetItem* s, const Q3TextFormat& f)
+ :name(n),style(s), format(f), alignment(Qt::AlignAuto), direction(QChar::DirON),liststyle(Q3StyleSheetItem::ListDisc) {
+ wsm = Q3StyleSheetItem::WhiteSpaceNormal;
+ }
+ QString name;
+ const Q3StyleSheetItem* style;
+ QString anchorHref;
+ Q3StyleSheetItem::WhiteSpaceMode wsm;
+ Q3TextFormat format;
+ signed int alignment : 16;
+ signed int direction : 5;
+ Q3StyleSheetItem::ListStyle liststyle;
+
+ Q3TextDocumentTag( const Q3TextDocumentTag& t) {
+ name = t.name;
+ style = t.style;
+ anchorHref = t.anchorHref;
+ wsm = t.wsm;
+ format = t.format;
+ alignment = t.alignment;
+ direction = t.direction;
+ liststyle = t.liststyle;
+ }
+ Q3TextDocumentTag& operator=(const Q3TextDocumentTag& t) {
+ name = t.name;
+ style = t.style;
+ anchorHref = t.anchorHref;
+ wsm = t.wsm;
+ format = t.format;
+ alignment = t.alignment;
+ direction = t.direction;
+ liststyle = t.liststyle;
+ return *this;
+ }
+
+ Q_DUMMY_COMPARISON_OPERATOR(Q3TextDocumentTag)
+};
+
+
+#define NEWPAR \
+ do{ \
+ if (!hasNewPar) { \
+ if (!textEditMode && curpar && curpar->length()>1 \
+ && curpar->at(curpar->length()-2)->c == QChar::LineSeparator) \
+ curpar->remove(curpar->length()-2, 1); \
+ curpar = createParagraph(this, curpar, curpar->next()); \
+ styles.append(vec); \
+ vec = 0; \
+ } \
+ hasNewPar = true; \
+ curpar->rtext = true; \
+ curpar->align = curtag.alignment; \
+ curpar->lstyle = curtag.liststyle; \
+ curpar->litem = (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem); \
+ curpar->str->setDirection((QChar::Direction)curtag.direction); \
+ space = true; \
+ tabExpansionColumn = 0; \
+ delete vec; \
+ vec = new QVector<Q3StyleSheetItem *>(); \
+ for (QStack<Q3TextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it) \
+ vec->append(const_cast<Q3StyleSheetItem *>((*it).style)); \
+ vec->append(const_cast<Q3StyleSheetItem *>(curtag.style)); \
+ } while(false);
+
+
+void Q3TextDocument::setRichText(const QString &text, const QString &context, const Q3TextFormat *initialFormat)
+{
+ preferRichText = true;
+ if (!context.isEmpty())
+ setContext(context);
+ clear();
+ fParag = lParag = createParagraph(this);
+ oTextValid = true;
+ oText = text;
+ setRichTextInternal(text, 0, initialFormat);
+ fParag->rtext = true;
+}
+
+void Q3TextDocument::setRichTextInternal(const QString &text, Q3TextCursor* cursor, const Q3TextFormat *initialFormat)
+{
+ Q3TextParagraph* curpar = lParag;
+ int pos = 0;
+ QStack<Q3TextDocumentTag> tags;
+ if (!initialFormat)
+ initialFormat = formatCollection()->defaultFormat();
+ Q3TextDocumentTag initag(QLatin1String(""), sheet_->item(QLatin1String("")), *initialFormat);
+ if (bodyText.isValid())
+ initag.format.setColor(bodyText);
+ Q3TextDocumentTag curtag = initag;
+ bool space = true;
+ bool canMergeLi = false;
+
+ bool textEditMode = false;
+ int tabExpansionColumn = 0;
+
+ const QChar* doc = text.unicode();
+ int length = text.length();
+ bool hasNewPar = curpar->length() <= 1;
+ QString anchorName;
+
+ // style sheet handling for margin and line spacing calculation below
+ Q3TextParagraph* stylesPar = curpar;
+ QVector<Q3StyleSheetItem *>* vec = 0;
+ QList< QVector<Q3StyleSheetItem *> *> styles;
+
+ if (cursor) {
+ cursor->splitAndInsertEmptyParagraph();
+ Q3TextCursor tmp = *cursor;
+ tmp.gotoPreviousLetter();
+ stylesPar = curpar = tmp.paragraph();
+ hasNewPar = true;
+ textEditMode = true;
+ } else {
+ NEWPAR;
+ }
+
+ // set rtext spacing to false for the initial paragraph.
+ curpar->rtext = false;
+
+ QString wellKnownTags = QLatin1String("br hr wsp table qt body meta title");
+
+ while (pos < length) {
+ if (hasPrefix(doc, length, pos, QLatin1Char('<'))){
+ if (!hasPrefix(doc, length, pos+1, QLatin1Char('/'))) {
+ // open tag
+ QMap<QString, QString> attr;
+ QMap<QString, QString>::Iterator it, end = attr.end();
+ bool emptyTag = false;
+ QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
+ if (tagname.isEmpty())
+ continue; // nothing we could do with this, probably parse error
+
+ const Q3StyleSheetItem* nstyle = sheet_->item(tagname);
+
+ if (nstyle) {
+ // we might have to close some 'forgotten' tags
+ while (!nstyle->allowedInContext(curtag.style)) {
+ QString msg;
+ msg.sprintf("QText Warning: Document not valid ('%s' not allowed in '%s' #%d)",
+ tagname.ascii(), curtag.style->name().ascii(), pos);
+ sheet_->error(msg);
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+
+ /* special handling for p and li for HTML
+ compatibility. We do not want to embed blocks in
+ p, and we do not want new blocks inside non-empty
+ lis. Plus we want to merge empty lis sometimes. */
+ if(nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ canMergeLi = true;
+ } else if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock) {
+ while (curtag.style->name() == QLatin1String("p")) {
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+
+ if (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ // we are in a li and a new block comes along
+ if (nstyle->name() == QLatin1String("ul") || nstyle->name() == QLatin1String("ol"))
+ hasNewPar = false; // we want an empty li (like most browsers)
+ if (!hasNewPar) {
+ /* do not add new blocks inside
+ non-empty lis */
+ while (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+ } else if (canMergeLi) {
+ /* we have an empty li and a block
+ comes along, merge them */
+ nstyle = curtag.style;
+ }
+ canMergeLi = false;
+ }
+ }
+ }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem* custom = 0;
+#else
+ bool custom = false;
+#endif
+
+ // some well-known tags, some have a nstyle, some not
+ if (wellKnownTags.contains(tagname)) {
+ if (tagname == QLatin1String("br")) {
+ emptyTag = space = true;
+ int index = qMax(curpar->length(),1) - 1;
+ Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
+ curpar->append(QString(QChar(QChar::LineSeparator)));
+ curpar->setFormat(index, 1, &format);
+ hasNewPar = false;
+ } else if (tagname == QLatin1String("hr")) {
+ emptyTag = space = true;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
+#endif
+ } else if (tagname == QLatin1String("table")) {
+ emptyTag = space = true;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor);
+ curpar->setAlignment(curtag.alignment);
+ custom = parseTable(attr, format, doc, length, pos, curpar);
+#endif
+ } else if (tagname == QLatin1String("qt") || tagname == QLatin1String("body")) {
+ it = attr.find(QLatin1String("bgcolor"));
+ if (it != end) {
+ QBrush *b = new QBrush(QColor(*it));
+ setPaper(b);
+ }
+ it = attr.find(QLatin1String("background"));
+ if (it != end) {
+#ifndef QT_NO_MIME
+ QImage img;
+ QString bg = *it;
+ const QMimeSource* m = factory_->data(bg, contxt);
+ if (!m) {
+ qCritical("QRichText: no mimesource for %s",
+ QFile::encodeName(bg).data());
+ } else {
+ if (!Q3ImageDrag::decode(m, img)) {
+ qCritical("Q3TextImage: cannot decode %s",
+ QFile::encodeName(bg).data());
+ }
+ }
+ if (!img.isNull()) {
+ QBrush *b = new QBrush(QColor(), QPixmap(img));
+ setPaper(b);
+ }
+#endif
+ }
+ it = attr.find(QLatin1String("text"));
+ if (it != end) {
+ QColor c(*it);
+ initag.format.setColor(c);
+ curtag.format.setColor(c);
+ bodyText = c;
+ }
+ it = attr.find(QLatin1String("link"));
+ if (it != end)
+ linkColor = QColor(*it);
+ it = attr.find(QLatin1String("title"));
+ if (it != end)
+ attribs.insert(QLatin1String("title"), *it);
+
+ if (textEditMode) {
+ it = attr.find(QLatin1String("style"));
+ if (it != end) {
+ QString a = *it;
+ int count = a.count(QLatin1Char(';')) + 1;
+ for (int s = 0; s < count; s++) {
+ QString style = a.section(QLatin1Char(';'), s, s);
+ if (style.startsWith(QLatin1String("font-size:")) && style.endsWith(QLatin1String("pt"))) {
+ scaleFontsFactor = double(formatCollection()->defaultFormat()->fn.pointSize()) /
+ style.mid(10, style.length() - 12).toInt();
+ }
+ }
+ }
+ nstyle = 0; // ignore body in textEditMode
+ }
+ // end qt- and body-tag handling
+ } else if (tagname == QLatin1String("meta")) {
+ if (attr[QLatin1String("name")] == QLatin1String("qrichtext") && attr[QLatin1String("content")] == QLatin1String("1"))
+ textEditMode = true;
+ } else if (tagname == QLatin1String("title")) {
+ QString title;
+ while (pos < length) {
+ if (hasPrefix(doc, length, pos, QLatin1Char('<')) && hasPrefix(doc, length, pos+1, QLatin1Char('/')) &&
+ parseCloseTag(doc, length, pos) == QLatin1String("title"))
+ break;
+ title += doc[pos];
+ ++pos;
+ }
+ attribs.insert(QLatin1String("title"), title);
+ }
+ } // end of well-known tag handling
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!custom) // try generic custom item
+ custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
+#endif
+ if (!nstyle && !custom) // we have no clue what this tag could be, ignore it
+ continue;
+
+ if (custom) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ int index = qMax(curpar->length(),1) - 1;
+ Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
+ curpar->append(QString(QLatin1Char('*')));
+ Q3TextFormat* f = formatCollection()->format(&format);
+ curpar->setFormat(index, 1, f);
+ curpar->at(index)->setCustomItem(custom);
+ if (!curtag.anchorHref.isEmpty())
+ curpar->at(index)->setAnchor(QString(), curtag.anchorHref);
+ if (!anchorName.isEmpty() ) {
+ curpar->at(index)->setAnchor(anchorName, curpar->at(index)->anchorHref());
+ anchorName.clear();
+ }
+ registerCustomItem(custom, curpar);
+ hasNewPar = false;
+#endif
+ } else if (!emptyTag) {
+ /* if we do nesting, push curtag on the stack,
+ otherwise reinint curag. */
+ if (curtag.style->name() != tagname || nstyle->selfNesting()) {
+ tags.push(curtag);
+ } else {
+ if (!tags.isEmpty())
+ curtag = tags.top();
+ else
+ curtag = initag;
+ }
+
+ curtag.name = tagname;
+ curtag.style = nstyle;
+ curtag.name = tagname;
+ curtag.style = nstyle;
+ if (nstyle->whiteSpaceMode() != Q3StyleSheetItem::WhiteSpaceModeUndefined)
+ curtag.wsm = nstyle->whiteSpaceMode();
+
+ /* netscape compatibility: eat a newline and only a newline if a pre block starts */
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre &&
+ nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock)
+ eat(doc, length, pos, QLatin1Char('\n'));
+
+ /* ignore whitespace for inline elements if there
+ was already one*/
+ if (!textEditMode &&
+ (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal
+ || curtag.wsm == Q3StyleSheetItem::WhiteSpaceNoWrap)
+ && (space || nstyle->displayMode() != Q3StyleSheetItem::DisplayInline))
+ eatSpace(doc, length, pos);
+
+ curtag.format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
+ if (nstyle->isAnchor()) {
+ if (!anchorName.isEmpty())
+ anchorName += QLatin1Char('#') + attr[QLatin1String("name")];
+ else
+ anchorName = attr[QLatin1String("name")];
+ curtag.anchorHref = attr[QLatin1String("href")];
+ }
+
+ if (nstyle->alignment() != Q3StyleSheetItem::Undefined)
+ curtag.alignment = nstyle->alignment();
+
+ if (nstyle->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
+ curtag.liststyle = nstyle->listStyle();
+
+ if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock
+ || nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+
+ if (nstyle->name() == QLatin1String("ol") ||
+ nstyle->name() == QLatin1String("ul") ||
+ nstyle->name() == QLatin1String("li")) {
+ QString type = attr[QLatin1String("type")];
+ if (!type.isEmpty()) {
+ if (type == QLatin1String("1")) {
+ curtag.liststyle = Q3StyleSheetItem::ListDecimal;
+ } else if (type == QLatin1String("a")) {
+ curtag.liststyle = Q3StyleSheetItem::ListLowerAlpha;
+ } else if (type == QLatin1String("A")) {
+ curtag.liststyle = Q3StyleSheetItem::ListUpperAlpha;
+ } else {
+ type = type.toLower();
+ if (type == QLatin1String("square"))
+ curtag.liststyle = Q3StyleSheetItem::ListSquare;
+ else if (type == QLatin1String("disc"))
+ curtag.liststyle = Q3StyleSheetItem::ListDisc;
+ else if (type == QLatin1String("circle"))
+ curtag.liststyle = Q3StyleSheetItem::ListCircle;
+ }
+ }
+ }
+
+
+ /* Internally we treat ordered and bullet
+ lists the same for margin calculations. In
+ order to have fast pointer compares in the
+ xMargin() functions we restrict ourselves to
+ <ol>. Once we calculate the margins in the
+ parser rathern than later, the unelegance of
+ this approach goes awy
+ */
+ if (nstyle->name() == QLatin1String("ul"))
+ curtag.style = sheet_->item(QLatin1String("ol"));
+
+ it = attr.find(QLatin1String("align"));
+ if (it != end) {
+ QString align = (*it).toLower();
+ if (align == QLatin1String("center"))
+ curtag.alignment = Qt::AlignCenter;
+ else if (align == QLatin1String("right"))
+ curtag.alignment = Qt::AlignRight;
+ else if (align == QLatin1String("justify"))
+ curtag.alignment = Qt::AlignJustify;
+ }
+ it = attr.find(QLatin1String("dir"));
+ if (it != end) {
+ QString dir = (*it).toLower();
+ if (dir == QLatin1String("rtl"))
+ curtag.direction = QChar::DirR;
+ else if (dir == QLatin1String("ltr"))
+ curtag.direction = QChar::DirL;
+ }
+
+ NEWPAR;
+
+ if (curtag.style && curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ it = attr.find(QLatin1String("value"));
+ if (it != end)
+ curpar->setListValue((*it).toInt());
+ }
+
+ it = attr.find(QLatin1String("style"));
+ if (it != end) {
+ QString a = *it;
+ bool ok = true;
+ int count = a.count(QLatin1Char(';'))+1;
+ for (int s = 0; ok && s < count; s++) {
+ QString style = a.section(QLatin1Char(';'), s, s);
+ if (style.startsWith(QLatin1String("margin-top:")) && style.endsWith(QLatin1String("px")))
+ curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok);
+ else if (style.startsWith(QLatin1String("margin-bottom:")) && style.endsWith(QLatin1String("px")))
+ curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok);
+ else if (style.startsWith(QLatin1String("margin-left:")) && style.endsWith(QLatin1String("px")))
+ curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok);
+ else if (style.startsWith(QLatin1String("margin-right:")) && style.endsWith(QLatin1String("px")))
+ curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok);
+ else if (style.startsWith(QLatin1String("text-indent:")) && style.endsWith(QLatin1String("px")))
+ curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok);
+ }
+ if (!ok) // be pressmistic
+ curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0;
+ }
+ } else if (nstyle->name() == QLatin1String("html")) {
+ it = attr.find(QLatin1String("dir"));
+ if (it != end) {
+ QString dir = (*it).toLower();
+ if (dir == QLatin1String("rtl"))
+ curtag.direction = QChar::DirR;
+ else if (dir == QLatin1String("ltr"))
+ curtag.direction = QChar::DirL;
+ }
+ }
+ }
+ } else {
+ QString tagname = parseCloseTag(doc, length, pos);
+ if (tagname.isEmpty())
+ continue; // nothing we could do with this, probably parse error
+ if (!sheet_->item(tagname)) // ignore unknown tags
+ continue;
+ if (tagname == QLatin1String("li"))
+ continue;
+
+ // we close a block item. Since the text may continue, we need to have a new paragraph
+ bool needNewPar = curtag.style->displayMode() == Q3StyleSheetItem::DisplayBlock
+ || curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem;
+
+
+ // html slopiness: handle unbalanched tag closing
+ while (curtag.name != tagname) {
+ QString msg;
+ msg.sprintf("QText Warning: Document not valid ('%s' not closed before '%s' #%d)",
+ curtag.name.ascii(), tagname.ascii(), pos);
+ sheet_->error(msg);
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+
+
+ // close the tag
+ if (!tags.isEmpty())
+ curtag = tags.pop();
+ else
+ curtag = initag;
+
+ if (needNewPar) {
+ if (textEditMode && (tagname == QLatin1String("p") || tagname == QLatin1String("div"))) // preserve empty paragraphs
+ hasNewPar = false;
+ NEWPAR;
+ }
+ }
+ } else {
+ // normal contents
+ QString s;
+ QChar c;
+ while (pos < length && !hasPrefix(doc, length, pos, QLatin1Char('<'))){
+ if (textEditMode) {
+ // text edit mode: we handle all white space but ignore newlines
+ c = parseChar(doc, length, pos, Q3StyleSheetItem::WhiteSpacePre);
+ if (c == QChar::LineSeparator)
+ break;
+ } else {
+ int l = pos;
+ c = parseChar(doc, length, pos, curtag.wsm);
+
+ // in white space pre mode: treat any space as non breakable
+ // and expand tabs to eight character wide columns.
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre) {
+ if (c == QLatin1Char('\t')) {
+ c = QLatin1Char(' ');
+ while((++tabExpansionColumn)%8)
+ s += c;
+ }
+ if (c == QChar::LineSeparator)
+ tabExpansionColumn = 0;
+ else
+ tabExpansionColumn++;
+
+ }
+ if (c == QLatin1Char(' ') || c == QChar::LineSeparator) {
+ /* avoid overlong paragraphs by forcing a new
+ paragraph after 4096 characters. This case can
+ occur when loading undiscovered plain text
+ documents in rich text mode. Instead of hanging
+ forever, we do the trick.
+ */
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && s.length() > 4096) do {
+ if (doc[l] == QLatin1Char('\n')) {
+ hasNewPar = false; // for a new paragraph ...
+ NEWPAR;
+ hasNewPar = false; // ... and make it non-reusable
+ c = QLatin1Char('\n'); // make sure we break below
+ break;
+ }
+ } while (++l < pos);
+ }
+ }
+
+ if (c == QLatin1Char('\n'))
+ break; // break on newlines, pre delievers a QChar::LineSeparator
+
+ bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode;
+
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && c_isSpace && space)
+ continue;
+ if (c == QLatin1Char('\r'))
+ continue;
+ space = c_isSpace;
+ s += c;
+ }
+ if (!s.isEmpty() && curtag.style->displayMode() != Q3StyleSheetItem::DisplayNone) {
+ hasNewPar = false;
+ int index = qMax(curpar->length(),1) - 1;
+ curpar->append(s);
+ if (curtag.wsm != Q3StyleSheetItem::WhiteSpaceNormal) {
+ Q3TextString *str = curpar->string();
+ for (int i = index; i < index + s.length(); ++i)
+ str->at(i).nobreak = true;
+ }
+
+ Q3TextFormat* f = formatCollection()->format(&curtag.format);
+ curpar->setFormat(index, s.length(), f, false); // do not use collection because we have done that already
+ f->ref += s.length() -1; // that what friends are for...
+ if (!curtag.anchorHref.isEmpty()) {
+ for (int i = 0; i < int(s.length()); i++)
+ curpar->at(index + i)->setAnchor(QString(), curtag.anchorHref);
+ }
+ if (!anchorName.isEmpty() ) {
+ for (int i = 0; i < int(s.length()); i++)
+ curpar->at(index + i)->setAnchor(anchorName, curpar->at(index + i)->anchorHref());
+ anchorName.clear();
+ }
+ }
+ }
+ }
+
+ if (hasNewPar && curpar != fParag && !cursor && stylesPar != curpar) {
+ // cleanup unused last paragraphs
+ curpar = curpar->p;
+ delete curpar->n;
+ }
+
+ if (!anchorName.isEmpty() ) {
+ curpar->at(curpar->length() - 1)->setAnchor(anchorName, curpar->at(curpar->length() - 1)->anchorHref());
+ anchorName.clear();
+ }
+
+ setRichTextMarginsInternal(styles, stylesPar);
+
+ if (cursor) {
+ cursor->gotoPreviousLetter();
+ cursor->remove();
+ }
+ while (!styles.isEmpty())
+ delete styles.takeFirst();
+ delete vec;
+}
+
+void Q3TextDocument::setRichTextMarginsInternal(QList< QVector<Q3StyleSheetItem *> *>& styles, Q3TextParagraph* stylesPar)
+{
+ // margin and line spacing calculation
+ // qDebug("setRichTextMarginsInternal: styles.size() = %d", styles.size());
+ QVector<Q3StyleSheetItem *>* prevStyle = 0;
+ int stylesIndex = 0;
+ QVector<Q3StyleSheetItem *>* curStyle = styles.size() ? styles.first() : 0;
+ QVector<Q3StyleSheetItem *>* nextStyle =
+ (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
+ while (stylesPar) {
+ if (!curStyle) {
+ stylesPar = stylesPar->next();
+ prevStyle = curStyle;
+ curStyle = nextStyle;
+ nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
+ continue;
+ }
+
+ int i, mar;
+ Q3StyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0;
+ if (mainStyle && mainStyle->displayMode() == Q3StyleSheetItem::DisplayListItem)
+ stylesPar->setListItem(true);
+ int numLists = 0;
+ for (i = 0; i < (int)curStyle->size(); ++i) {
+ if ((*curStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
+ && (*curStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
+ numLists++;
+ }
+ stylesPar->ldepth = numLists;
+ if (stylesPar->next() && nextStyle) {
+ // also set the depth of the next paragraph, required for the margin calculation
+ numLists = 0;
+ for (i = 0; i < (int)nextStyle->size(); ++i) {
+ if ((*nextStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
+ && (*nextStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
+ numLists++;
+ }
+ stylesPar->next()->ldepth = numLists;
+ }
+
+ // do the top margin
+ Q3StyleSheetItem* item = mainStyle;
+ int m;
+ if (stylesPar->utm > 0) {
+ m = stylesPar->utm-1;
+ stylesPar->utm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
+ if (stylesPar->ldepth) {
+ if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
+ m /= stylesPar->ldepth * stylesPar->ldepth;
+ else
+ m = 0;
+ }
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ if (prevStyle && i < (int) prevStyle->size() &&
+ ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
+ (*prevStyle)[i] == item))
+ break;
+ // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
+ if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined &&
+ (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
+ continue;
+ mar = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
+ m = qMax(m, mar);
+ }
+ stylesPar->utm = m - stylesPar->topMargin();
+
+ // do the bottom margin
+ item = mainStyle;
+ if (stylesPar->ubm > 0) {
+ m = stylesPar->ubm-1;
+ stylesPar->ubm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
+ if (stylesPar->ldepth) {
+ if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
+ m /= stylesPar->ldepth * stylesPar->ldepth;
+ else
+ m = 0;
+ }
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ if (nextStyle && i < (int) nextStyle->size() &&
+ ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
+ (*nextStyle)[i] == item))
+ break;
+ // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
+ if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined &&
+ (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
+ continue;
+ mar = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
+ m = qMax(m, mar);
+ }
+ stylesPar->ubm = m - stylesPar->bottomMargin();
+
+ // do the left margin, simplyfied
+ item = mainStyle;
+ if (stylesPar->ulm > 0) {
+ m = stylesPar->ulm-1;
+ stylesPar->ulm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ m += qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
+ }
+ stylesPar->ulm = m - stylesPar->leftMargin();
+
+ // do the right margin, simplyfied
+ item = mainStyle;
+ if (stylesPar->urm > 0) {
+ m = stylesPar->urm-1;
+ stylesPar->urm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ m += qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
+ }
+ stylesPar->urm = m - stylesPar->rightMargin();
+
+ // do the first line margin, which really should be called text-indent
+ item = mainStyle;
+ if (stylesPar->uflm > 0) {
+ m = stylesPar->uflm-1;
+ stylesPar->uflm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ mar = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
+ m = qMax(m, mar);
+ }
+ stylesPar->uflm =m - stylesPar->firstLineMargin();
+
+ // do the bogus line "spacing", which really is just an extra margin
+ item = mainStyle;
+ for (i = (int)curStyle->size() - 1 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ if (item->lineSpacing() != Q3StyleSheetItem::Undefined) {
+ stylesPar->ulinespacing = item->lineSpacing();
+ if (formatCollection() &&
+ stylesPar->ulinespacing < formatCollection()->defaultFormat()->height())
+ stylesPar->ulinespacing += formatCollection()->defaultFormat()->height();
+ break;
+ }
+ }
+
+ stylesPar = stylesPar->next();
+ prevStyle = curStyle;
+ curStyle = nextStyle;
+ nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
+ }
+}
+
+void Q3TextDocument::setText(const QString &text, const QString &context)
+{
+ focusIndicator.parag = 0;
+ selections.clear();
+ if ((txtFormat == Qt::AutoText && Q3StyleSheet::mightBeRichText(text))
+ || txtFormat == Qt::RichText)
+ setRichText(text, context);
+ else
+ setPlainText(text);
+}
+
+QString Q3TextDocument::plainText() const
+{
+ QString buffer;
+ QString s;
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ if (!p->mightHaveCustomItems) {
+ const Q3TextString *ts = p->string(); // workaround VC++ and Borland
+ s = ts->toString(); // with false we don't fix spaces (nbsp)
+ } else {
+ for (int i = 0; i < p->length() - 1; ++i) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ s += QLatin1String("\n");
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ s += c->richText()->plainText() + QLatin1String("\n");
+ }
+ s += QLatin1String("\n");
+ }
+ } else
+#endif
+ {
+ s += p->at(i)->c;
+ }
+ }
+ }
+ s.remove(s.length() - 1, 1);
+ if (p->next())
+ s += QLatin1String("\n");
+ buffer += s;
+ p = p->next();
+ }
+ return buffer;
+}
+
+static QString align_to_string(int a)
+{
+ if (a & Qt::AlignRight)
+ return QLatin1String(" align=\"right\"");
+ if (a & Qt::AlignHCenter)
+ return QLatin1String(" align=\"center\"");
+ if (a & Qt::AlignJustify)
+ return QLatin1String(" align=\"justify\"");
+ return QString();
+}
+
+static QString direction_to_string(int dir)
+{
+ if (dir != QChar::DirON)
+ return (dir == QChar::DirL? QLatin1String(" dir=\"ltr\"") : QLatin1String(" dir=\"rtl\""));
+ return QString();
+}
+
+static QString list_value_to_string(int v)
+{
+ if (v != -1)
+ return QLatin1String(" listvalue=\"") + QString::number(v) + QLatin1Char('"');
+ return QString();
+}
+
+static QString list_style_to_string(int v)
+{
+ switch(v) {
+ case Q3StyleSheetItem::ListDecimal: return QLatin1String("\"1\"");
+ case Q3StyleSheetItem::ListLowerAlpha: return QLatin1String("\"a\"");
+ case Q3StyleSheetItem::ListUpperAlpha: return QLatin1String("\"A\"");
+ case Q3StyleSheetItem::ListDisc: return QLatin1String("\"disc\"");
+ case Q3StyleSheetItem::ListSquare: return QLatin1String("\"square\"");
+ case Q3StyleSheetItem::ListCircle: return QLatin1String("\"circle\"");
+ default:
+ return QString();
+ }
+}
+
+static inline bool list_is_ordered(int v)
+{
+ return v == Q3StyleSheetItem::ListDecimal ||
+ v == Q3StyleSheetItem::ListLowerAlpha ||
+ v == Q3StyleSheetItem::ListUpperAlpha;
+}
+
+
+static QString margin_to_string(Q3StyleSheetItem* style, int t, int b, int l, int r, int fl)
+{
+ QString s;
+ if (l > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-left:") +
+ QString::number(l+qMax(0,style->margin(Q3StyleSheetItem::MarginLeft))) + QLatin1String("px");
+ if (r > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-right:") +
+ QString::number(r+qMax(0,style->margin(Q3StyleSheetItem::MarginRight))) + QLatin1String("px");
+ if (t > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-top:") +
+ QString::number(t+qMax(0,style->margin(Q3StyleSheetItem::MarginTop))) + QLatin1String("px");
+ if (b > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-bottom:") +
+ QString::number(b+qMax(0,style->margin(Q3StyleSheetItem::MarginBottom))) + QLatin1String("px");
+ if (fl > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("text-indent:") +
+ QString::number(fl+qMax(0,style->margin(Q3StyleSheetItem::MarginFirstLine))) + QLatin1String("px");
+ if (s.size())
+ return QLatin1String(" style=\"") + s + QLatin1String("\"");
+ return QString();
+}
+
+QString Q3TextDocument::richText() const
+{
+ QString s = QLatin1String("");
+ if (!par) {
+ s += QLatin1String("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body style=\"font-size:");
+ s += QString::number(formatCollection()->defaultFormat()->font().pointSize());
+ s += QLatin1String("pt;font-family:");
+ s += formatCollection()->defaultFormat()->font().family();
+ s += QLatin1String("\">");
+ }
+ Q3TextParagraph* p = fParag;
+
+ Q3StyleSheetItem* item_p = styleSheet()->item(QLatin1String("p"));
+ Q3StyleSheetItem* item_div = styleSheet()->item(QLatin1String("div"));
+ Q3StyleSheetItem* item_ul = styleSheet()->item(QLatin1String("ul"));
+ Q3StyleSheetItem* item_ol = styleSheet()->item(QLatin1String("ol"));
+ Q3StyleSheetItem* item_li = styleSheet()->item(QLatin1String("li"));
+ if (!item_p || !item_div || !item_ul || !item_ol || !item_li) {
+ qWarning("QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, div, ul, ol, or li)");
+ return QString();
+ }
+ int pastListDepth = 0;
+ int listDepth = 0;
+#if 0
+ int futureListDepth = 0;
+#endif
+ QVector<int> listStyles(10);
+
+ while (p) {
+ listDepth = p->listDepth();
+ if (listDepth < pastListDepth) {
+ for (int i = pastListDepth; i > listDepth; i--)
+ s += list_is_ordered(listStyles[i]) ? QLatin1String("</ol>") : QLatin1String("</ul>");
+ s += QLatin1Char('\n');
+ } else if (listDepth > pastListDepth) {
+ s += QLatin1Char('\n');
+ listStyles.resize(qMax((int)listStyles.size(), listDepth+1));
+ QString list_type;
+ listStyles[listDepth] = p->listStyle();
+ if (!list_is_ordered(p->listStyle()) || item_ol->listStyle() != p->listStyle())
+ list_type = QLatin1String(" type=") + list_style_to_string(p->listStyle());
+ for (int i = pastListDepth; i < listDepth; i++) {
+ s += list_is_ordered(p->listStyle()) ? QLatin1String("<ol") : QLatin1String("<ul");
+ s += list_type + QLatin1Char('>');
+ }
+ } else {
+ s += QLatin1Char('\n');
+ }
+
+ QString ps = p->richText();
+
+#if 0
+ // for the bottom margin we need to know whether we are at the end of a list
+ futureListDepth = 0;
+ if (listDepth > 0 && p->next())
+ futureListDepth = p->next()->listDepth();
+#endif
+
+ if (richTextExportStart && richTextExportStart->paragraph() ==p &&
+ richTextExportStart->index() == 0)
+ s += QLatin1String("<!--StartFragment-->");
+
+ if (p->isListItem()) {
+ s += QLatin1String("<li");
+ if (p->listStyle() != listStyles[listDepth])
+ s += QLatin1String(" type=") + list_style_to_string(p->listStyle());
+ s += align_to_string(p->alignment());
+ s += margin_to_string(item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
+ s += list_value_to_string(p->listValue());
+ s += direction_to_string(p->direction());
+ s += QLatin1Char('>');
+ s += ps;
+ s += QLatin1String("</li>");
+ } else if (p->listDepth()) {
+ s += QLatin1String("<div");
+ s += align_to_string(p->alignment());
+ s += margin_to_string(item_div, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
+ s += direction_to_string(p->direction());
+ s += QLatin1Char('>');
+ s += ps;
+ s += QLatin1String("</div>");
+ } else {
+ // normal paragraph item
+ s += QLatin1String("<p");
+ s += align_to_string(p->alignment());
+ s += margin_to_string(item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
+ s += direction_to_string(p->direction());
+ s += QLatin1Char('>');
+ s += ps;
+ s += QLatin1String("</p>");
+ }
+ pastListDepth = listDepth;
+ p = p->next();
+ }
+ while (listDepth > 0) {
+ s += list_is_ordered(listStyles[listDepth]) ? QLatin1String("</ol>") : QLatin1String("</ul>");
+ listDepth--;
+ }
+
+ if (!par)
+ s += QLatin1String("\n</body></html>\n");
+
+ return s;
+}
+
+QString Q3TextDocument::text() const
+{
+ if ((txtFormat == Qt::AutoText && preferRichText) || txtFormat == Qt::RichText)
+ return richText();
+ return plainText();
+}
+
+QString Q3TextDocument::text(int parag) const
+{
+ Q3TextParagraph *p = paragAt(parag);
+ if (!p)
+ return QString();
+
+ if ((txtFormat == Qt::AutoText && preferRichText) || txtFormat == Qt::RichText)
+ return p->richText();
+ else
+ return p->string()->toString();
+}
+
+void Q3TextDocument::invalidate()
+{
+ Q3TextParagraph *s = fParag;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+void Q3TextDocument::selectionStart(int id, int &paragId, int &index)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+ Q3TextDocumentSelection &sel = *it;
+ paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
+ index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
+}
+
+Q3TextCursor Q3TextDocument::selectionStartCursor(int id)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return Q3TextCursor(this);
+ Q3TextDocumentSelection &sel = *it;
+ if (sel.swapped)
+ return sel.endCursor;
+ return sel.startCursor;
+}
+
+Q3TextCursor Q3TextDocument::selectionEndCursor(int id)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return Q3TextCursor(this);
+ Q3TextDocumentSelection &sel = *it;
+ if (!sel.swapped)
+ return sel.endCursor;
+ return sel.startCursor;
+}
+
+void Q3TextDocument::selectionEnd(int id, int &paragId, int &index)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+ Q3TextDocumentSelection &sel = *it;
+ paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
+ index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
+}
+
+void Q3TextDocument::addSelection(int id)
+{
+ nSelections = qMax(nSelections, id + 1);
+}
+
+static void setSelectionEndHelper(int id, Q3TextDocumentSelection &sel, Q3TextCursor &start, Q3TextCursor &end)
+{
+ Q3TextCursor c1 = start;
+ Q3TextCursor c2 = end;
+ if (sel.swapped) {
+ c1 = end;
+ c2 = start;
+ }
+
+ c1.paragraph()->removeSelection(id);
+ c2.paragraph()->removeSelection(id);
+ if (c1.paragraph() != c2.paragraph()) {
+ c1.paragraph()->setSelection(id, c1.index(), c1.paragraph()->length() - 1);
+ c2.paragraph()->setSelection(id, 0, c2.index());
+ } else {
+ c1.paragraph()->setSelection(id, qMin(c1.index(), c2.index()), qMax(c1.index(), c2.index()));
+ }
+
+ sel.startCursor = start;
+ sel.endCursor = end;
+ if (sel.startCursor.paragraph() == sel.endCursor.paragraph())
+ sel.swapped = sel.startCursor.index() > sel.endCursor.index();
+}
+
+bool Q3TextDocument::setSelectionEnd(int id, const Q3TextCursor &cursor)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return false;
+ Q3TextDocumentSelection &sel = *it;
+
+ Q3TextCursor start = sel.startCursor;
+ Q3TextCursor end = cursor;
+
+ if (start == end) {
+ removeSelection(id);
+ setSelectionStart(id, cursor);
+ return true;
+ }
+
+ if (sel.endCursor.paragraph() == end.paragraph()) {
+ setSelectionEndHelper(id, sel, start, end);
+ return true;
+ }
+
+ bool inSelection = false;
+ Q3TextCursor c(this);
+ Q3TextCursor tmp = sel.startCursor;
+ if (sel.swapped)
+ tmp = sel.endCursor;
+ tmp.restoreState();
+ Q3TextCursor tmp2 = cursor;
+ tmp2.restoreState();
+ c.setParagraph(tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph());
+ bool hadStart = false;
+ bool hadEnd = false;
+ bool hadStartParag = false;
+ bool hadEndParag = false;
+ bool hadOldStart = false;
+ bool hadOldEnd = false;
+ bool leftSelection = false;
+ sel.swapped = false;
+ for (;;) {
+ if (c == start)
+ hadStart = true;
+ if (c == end)
+ hadEnd = true;
+ if (c.paragraph() == start.paragraph())
+ hadStartParag = true;
+ if (c.paragraph() == end.paragraph())
+ hadEndParag = true;
+ if (c == sel.startCursor)
+ hadOldStart = true;
+ if (c == sel.endCursor)
+ hadOldEnd = true;
+
+ if (!sel.swapped &&
+ ((hadEnd && !hadStart)
+ || (hadEnd && hadStart && start.paragraph() == end.paragraph() && start.index() > end.index())))
+ sel.swapped = true;
+
+ if ((c == end && hadStartParag) || (c == start && hadEndParag)) {
+ Q3TextCursor tmp = c;
+ tmp.restoreState();
+ if (tmp.paragraph() != c.paragraph()) {
+ int sstart = tmp.paragraph()->selectionStart(id);
+ tmp.paragraph()->removeSelection(id);
+ tmp.paragraph()->setSelection(id, sstart, tmp.index());
+ }
+ }
+
+ if (inSelection &&
+ ((c == end && hadStart) || (c == start && hadEnd)))
+ leftSelection = true;
+ else if (!leftSelection && !inSelection && (hadStart || hadEnd))
+ inSelection = true;
+
+ bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection(id) && c.atParagEnd();
+ c.paragraph()->removeSelection(id);
+ if (inSelection) {
+ if (c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph()) {
+ c.paragraph()->setSelection(id, qMin(start.index(), end.index()), qMax(start.index(), end.index()));
+ } else if (c.paragraph() == start.paragraph() && !hadEndParag) {
+ c.paragraph()->setSelection(id, start.index(), c.paragraph()->length() - 1);
+ } else if (c.paragraph() == end.paragraph() && !hadStartParag) {
+ c.paragraph()->setSelection(id, end.index(), c.paragraph()->length() - 1);
+ } else if (c.paragraph() == end.paragraph() && hadEndParag) {
+ c.paragraph()->setSelection(id, 0, end.index());
+ } else if (c.paragraph() == start.paragraph() && hadStartParag) {
+ c.paragraph()->setSelection(id, 0, start.index());
+ } else {
+ c.paragraph()->setSelection(id, 0, c.paragraph()->length() - 1);
+ }
+ }
+
+ if (leftSelection)
+ inSelection = false;
+
+ if (noSelectionAnymore)
+ break;
+ // *ugle*hack optimization
+ Q3TextParagraph *p = c.paragraph();
+ if ( p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph()) {
+ c.gotoNextLetter();
+ if (p == lastParagraph() && c.atParagEnd())
+ break;
+ } else {
+ if (p->document()->parent())
+ do {
+ c.gotoNextLetter();
+ } while (c.paragraph() == p);
+ else
+ c.setParagraph(p->next());
+ }
+ }
+
+ if (!sel.swapped)
+ sel.startCursor.paragraph()->setSelection(id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1);
+
+ sel.startCursor = start;
+ sel.endCursor = end;
+ if (sel.startCursor.paragraph() == sel.endCursor.paragraph())
+ sel.swapped = sel.startCursor.index() > sel.endCursor.index();
+
+ setSelectionEndHelper(id, sel, start, end);
+
+ return true;
+}
+
+void Q3TextDocument::selectAll(int id)
+{
+ removeSelection(id);
+
+ Q3TextDocumentSelection sel;
+ sel.swapped = false;
+ Q3TextCursor c(this);
+
+ c.setParagraph(fParag);
+ c.setIndex(0);
+ sel.startCursor = c;
+
+ c.setParagraph(lParag);
+ c.setIndex(lParag->length() - 1);
+ sel.endCursor = c;
+
+ selections.insert(id, sel);
+
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ p->setSelection(id, 0, p->length() - 1);
+ p = p->next();
+ }
+
+ for (int idx = 0; idx < childList.size(); ++idx) {
+ Q3TextDocument *dc = childList.at(idx);
+ dc->selectAll(id);
+ }
+}
+
+bool Q3TextDocument::removeSelection(int id)
+{
+ if (!selections.contains(id))
+ return false;
+
+ Q3TextDocumentSelection &sel = selections[id];
+
+ Q3TextCursor start = sel.swapped ? sel.endCursor : sel.startCursor;
+ Q3TextCursor end = sel.swapped ? sel.startCursor : sel.endCursor;
+ Q3TextParagraph* p = 0;
+ while (start != end) {
+ if (p != start.paragraph()) {
+ p = start.paragraph();
+ p->removeSelection(id);
+ //### avoid endless loop by all means necessary, did somebody mention refactoring?
+ if (!parent() && p == lParag)
+ break;
+ }
+ start.gotoNextLetter();
+ }
+ p = start.paragraph();
+ p->removeSelection(id);
+ selections.remove(id);
+ return true;
+}
+
+QString Q3TextDocument::selectedText(int id, bool asRichText) const
+{
+ QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.find(id);
+ if (it == selections.end())
+ return QString();
+
+ Q3TextDocumentSelection sel = *it;
+
+
+ Q3TextCursor c1 = sel.startCursor;
+ Q3TextCursor c2 = sel.endCursor;
+ if (sel.swapped) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ /* 3.0.3 improvement: Make it possible to get a reasonable
+ selection inside a table. This approach is very conservative:
+ make sure that both cursors have the same depth level and point
+ to paragraphs within the same text document.
+
+ Meaning if you select text in two table cells, you will get the
+ entire table. This is still far better than the 3.0.2, where
+ you always got the entire table.
+
+ ### Fix this properly when refactoring
+ */
+ while (c2.nestedDepth() > c1.nestedDepth())
+ c2.oneUp();
+ while (c1.nestedDepth() > c2.nestedDepth())
+ c1.oneUp();
+ while (c1.nestedDepth() && c2.nestedDepth() &&
+ c1.paragraph()->document() != c2.paragraph()->document()) {
+ c1.oneUp();
+ c2.oneUp();
+ }
+ // do not trust sel_swapped with tables. Fix this properly when refactoring as well
+ if (c1.paragraph()->paragId() > c2.paragraph()->paragId() ||
+ (c1.paragraph() == c2.paragraph() && c1.index() > c2.index())) {
+ Q3TextCursor tmp = c1;
+ c2 = c1;
+ c1 = tmp;
+ }
+
+ // end selection 3.0.3 improvement
+
+ if (asRichText && !parent()) {
+ richTextExportStart = &c1;
+ richTextExportEnd = &c2;
+
+ QString sel = richText();
+ int from = sel.indexOf(QLatin1String("<!--StartFragment-->"));
+ if (from >= 0) {
+ from += 20;
+ // find the previous span and move it into the start fragment before we clip it
+ QString prevspan;
+ int pspan = sel.lastIndexOf(QLatin1String("<span"), from-21);
+ if (pspan > sel.lastIndexOf(QLatin1String("</span"), from-21)) {
+ int spanend = sel.indexOf(QLatin1Char('>'), pspan);
+ prevspan = sel.mid(pspan, spanend - pspan + 1);
+ }
+ int to = sel.lastIndexOf(QLatin1String("<!--EndFragment-->"));
+ if (from <= to)
+ sel = QLatin1String("<!--StartFragment-->") + prevspan + sel.mid(from, to - from);
+ }
+ richTextExportStart = richTextExportEnd = 0;
+ return sel;
+ }
+
+ QString s;
+ if (c1.paragraph() == c2.paragraph()) {
+ Q3TextParagraph *p = c1.paragraph();
+ int end = c2.index();
+ if (p->at(qMax(0, end - 1))->isCustom())
+ ++end;
+ if (!p->mightHaveCustomItems) {
+ s += p->string()->toString().mid(c1.index(), end - c1.index());
+ } else {
+ for (int i = c1.index(); i < end; ++i) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ s += QLatin1String("\n");
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ s += c->richText()->plainText() + QLatin1String("\n");
+ }
+ s += QLatin1String("\n");
+ }
+ } else
+#endif
+ {
+ s += p->at(i)->c;
+ }
+ }
+ }
+ } else {
+ Q3TextParagraph *p = c1.paragraph();
+ int start = c1.index();
+ while (p) {
+ int end = p == c2.paragraph() ? c2.index() : p->length() - 1;
+ if (p == c2.paragraph() && p->at(qMax(0, end - 1))->isCustom())
+ ++end;
+ if (!p->mightHaveCustomItems) {
+ s += p->string()->toString().mid(start, end - start);
+ if (p != c2.paragraph())
+ s += QLatin1String("\n");
+ } else {
+ for (int i = start; i < end; ++i) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ s += QLatin1String(QLatin1String("\n"));
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ s += c->richText()->plainText() + QLatin1String("\n");
+ }
+ s += QLatin1String("\n");
+ }
+ } else
+#endif
+ {
+ s += p->at(i)->c;
+ }
+ }
+ }
+ start = 0;
+ if (p == c2.paragraph())
+ break;
+ p = p->next();
+ }
+ }
+ // ### workaround for plain text export until we get proper
+ // mime types: turn unicode line separators into the more
+ // widely understood \n. Makes copy and pasting code snipplets
+ // from within Assistent possible
+ QChar* uc = (QChar*) s.unicode();
+ for (int ii = 0; ii < s.length(); ii++) {
+ if (uc[(int)ii] == QChar::LineSeparator)
+ uc[(int)ii] = QLatin1Char('\n');
+ else if ( uc[(int)ii] == QChar::Nbsp )
+ uc[(int)ii] = QLatin1Char(' ');
+ }
+ return s;
+}
+
+void Q3TextDocument::setFormat(int id, Q3TextFormat *f, int flags)
+{
+ QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.constFind(id);
+ if (it == selections.constEnd())
+ return;
+
+ Q3TextDocumentSelection sel = *it;
+
+ Q3TextCursor c1 = sel.startCursor;
+ Q3TextCursor c2 = sel.endCursor;
+ if (sel.swapped) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ c2.restoreState();
+ c1.restoreState();
+
+ if (c1.paragraph() == c2.paragraph()) {
+ c1.paragraph()->setFormat(c1.index(), c2.index() - c1.index(), f, true, flags);
+ return;
+ }
+
+ c1.paragraph()->setFormat(c1.index(), c1.paragraph()->length() - c1.index(), f, true, flags);
+ Q3TextParagraph *p = c1.paragraph()->next();
+ while (p && p != c2.paragraph()) {
+ p->setFormat(0, p->length(), f, true, flags);
+ p = p->next();
+ }
+ c2.paragraph()->setFormat(0, c2.index(), f, true, flags);
+}
+
+void Q3TextDocument::removeSelectedText(int id, Q3TextCursor *cursor)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+
+ Q3TextDocumentSelection sel = *it;
+ Q3TextCursor c1 = sel.startCursor;
+ Q3TextCursor c2 = sel.endCursor;
+ if (sel.swapped) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ // ### no support for editing tables yet
+ if (c1.nestedDepth() || c2.nestedDepth())
+ return;
+
+ c2.restoreState();
+ c1.restoreState();
+
+ *cursor = c1;
+ removeSelection(id);
+
+ if (c1.paragraph() == c2.paragraph()) {
+ c1.paragraph()->remove(c1.index(), c2.index() - c1.index());
+ return;
+ }
+
+ if (c1.paragraph() == fParag && c1.index() == 0 &&
+ c2.paragraph() == lParag && c2.index() == lParag->length() - 1)
+ cursor->setValid(false);
+
+ bool didGoLeft = false;
+ if ( c1.index() == 0 && c1.paragraph() != fParag) {
+ cursor->gotoPreviousLetter();
+ didGoLeft = cursor->isValid();
+ }
+
+ c1.paragraph()->remove(c1.index(), c1.paragraph()->length() - 1 - c1.index());
+ Q3TextParagraph *p = c1.paragraph()->next();
+ int dy = 0;
+ Q3TextParagraph *tmp;
+ while (p && p != c2.paragraph()) {
+ tmp = p->next();
+ dy -= p->rect().height();
+ delete p;
+ p = tmp;
+ }
+ c2.paragraph()->remove(0, c2.index());
+ while (p) {
+ p->move(dy);
+ p->invalidate(0);
+ p->setEndState(-1);
+ p = p->next();
+ }
+
+
+ c1.paragraph()->join(c2.paragraph());
+
+ if (didGoLeft)
+ cursor->gotoNextLetter();
+}
+
+void Q3TextDocument::indentSelection(int id)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+
+ Q3TextDocumentSelection sel = *it;
+ Q3TextParagraph *startParag = sel.startCursor.paragraph();
+ Q3TextParagraph *endParag = sel.endCursor.paragraph();
+ if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) {
+ endParag = sel.startCursor.paragraph();
+ startParag = sel.endCursor.paragraph();
+ }
+
+ Q3TextParagraph *p = startParag;
+ while (p && p != endParag) {
+ p->indent();
+ p = p->next();
+ }
+}
+
+void Q3TextCommandHistory::clear()
+{
+ while (!history.isEmpty())
+ delete history.takeFirst();
+ current = -1;
+}
+
+void Q3TextDocument::addCommand(Q3TextCommand *cmd)
+{
+ commandHistory->addCommand(cmd);
+}
+
+Q3TextCursor *Q3TextDocument::undo(Q3TextCursor *c)
+{
+ return commandHistory->undo(c);
+}
+
+Q3TextCursor *Q3TextDocument::redo(Q3TextCursor *c)
+{
+ return commandHistory->redo(c);
+}
+
+bool Q3TextDocument::find(Q3TextCursor& cursor, const QString &expr, bool cs, bool wo, bool forward)
+{
+ Qt::CaseSensitivity caseSensitive = cs ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ removeSelection(Standard);
+ if (expr.isEmpty())
+ return false;
+ for (;;) {
+ QString s = cursor.paragraph()->string()->toString();
+ int start = cursor.index();
+ for (;;) {
+ int res = forward
+ ? s.indexOf(expr, start, caseSensitive)
+ : s.lastIndexOf(expr, start, caseSensitive);
+ int end = res + expr.length();
+ if (res == -1 || (!forward && start <= res))
+ break;
+ if (!wo || ((res == 0 || !s[res-1].isLetterOrNumber())
+ && (end == (int)s.length() || !s[end].isLetterOrNumber()))) {
+ removeSelection(Standard);
+ cursor.setIndex(forward ? end : res);
+ setSelectionStart(Standard, cursor);
+ cursor.setIndex(forward ? res : end);
+ setSelectionEnd(Standard, cursor);
+ if (!forward)
+ cursor.setIndex(res);
+ return true;
+ }
+ start = res + (forward ? 1 : -1);
+ }
+ if (forward) {
+ if (cursor.paragraph() == lastParagraph() && cursor.atParagEnd())
+ break;
+ cursor.gotoNextLetter();
+ } else {
+ if (cursor.paragraph() == firstParagraph() && cursor.atParagStart())
+ break;
+ cursor.gotoPreviousLetter();
+ }
+ }
+ return false;
+}
+
+void Q3TextDocument::setTextFormat(Qt::TextFormat f)
+{
+ txtFormat = f;
+ if (fParag == lParag && fParag->length() <= 1)
+ fParag->rtext = (f == Qt::RichText);
+}
+
+Qt::TextFormat Q3TextDocument::textFormat() const
+{
+ return txtFormat;
+}
+
+bool Q3TextDocument::inSelection(int selId, const QPoint &pos) const
+{
+ QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.find(selId);
+ if (it == selections.end())
+ return false;
+
+ Q3TextDocumentSelection sel = *it;
+ Q3TextParagraph *startParag = sel.startCursor.paragraph();
+ Q3TextParagraph *endParag = sel.endCursor.paragraph();
+ if (sel.startCursor.paragraph() == sel.endCursor.paragraph() &&
+ sel.startCursor.paragraph()->selectionStart(selId) == sel.endCursor.paragraph()->selectionEnd(selId))
+ return false;
+ if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) {
+ endParag = sel.startCursor.paragraph();
+ startParag = sel.endCursor.paragraph();
+ }
+
+ Q3TextParagraph *p = startParag;
+ while (p) {
+ if (p->rect().contains(pos)) {
+ bool inSel = false;
+ int selStart = p->selectionStart(selId);
+ int selEnd = p->selectionEnd(selId);
+ int y = 0;
+ int h = 0;
+ for (int i = 0; i < p->length(); ++i) {
+ if (i == selStart)
+ inSel = true;
+ if (i == selEnd)
+ break;
+ if (p->at(i)->lineStart) {
+ y = (*p->lineStarts.find(i))->y;
+ h = (*p->lineStarts.find(i))->h;
+ }
+ if (pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h) {
+ if (inSel && pos.x() >= p->at(i)->x &&
+ pos.x() <= p->at(i)->x + p->at(i)->format()->width(p->at(i)->c))
+ return true;
+ }
+ }
+ }
+ if (pos.y() < p->rect().y())
+ break;
+ if (p == endParag)
+ break;
+ p = p->next();
+ }
+
+ return false;
+}
+
+void Q3TextDocument::doLayout(QPainter *p, int w)
+{
+ minw = wused = 0;
+ if (!is_printer(p))
+ p = 0;
+ withoutDoubleBuffer = (p != 0);
+ QPainter * oldPainter = Q3TextFormat::painter();
+ Q3TextFormat::setPainter(p);
+ tStopWidth = formatCollection()->defaultFormat()->width( QLatin1Char('x') ) * 8;
+ flow_->setWidth(w);
+ cw = w;
+ vw = w;
+ Q3TextParagraph *parag = fParag;
+ while (parag) {
+ parag->invalidate(0);
+ if (p)
+ parag->adjustToPainter(p);
+ parag->format();
+ parag = parag->next();
+ }
+ Q3TextFormat::setPainter(oldPainter);
+}
+
+QPixmap *Q3TextDocument::bufferPixmap(const QSize &s)
+{
+ if (!buf_pixmap)
+ buf_pixmap = new QPixmap(s.expandedTo(QSize(1,1)));
+ else if (buf_pixmap->size() != s)
+ buf_pixmap->resize(s.expandedTo(buf_pixmap->size()));
+ return buf_pixmap;
+}
+
+void Q3TextDocument::draw(QPainter *p, const QRect &rect, const QPalette &pal,
+ const QBrush *paper)
+{
+ if (!firstParagraph())
+ return;
+
+ if (paper) {
+ p->setBrushOrigin(-qIntCast(p->translationX()),
+ -qIntCast(p->translationY()));
+
+ p->fillRect(rect, *paper);
+ }
+
+ QPainter * oldPainter = Q3TextFormat::painter();
+ Q3TextFormat::setPainter(p);
+
+ if (formatCollection()->defaultFormat()->color() != pal.text().color())
+ setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color());
+
+ Q3TextParagraph *parag = firstParagraph();
+ while (parag) {
+ if (!parag->isValid())
+ parag->format();
+ int y = parag->rect().y();
+ QRect pr(parag->rect());
+ pr.setX(0);
+ pr.setWidth(QWIDGETSIZE_MAX);
+ if (!rect.isNull() && !rect.intersects(pr)) {
+ parag = parag->next();
+ continue;
+ }
+ p->translate(0, y);
+ if (rect.isValid())
+ parag->paint(*p, pal, 0, false, rect.x(), rect.y(), rect.width(), rect.height());
+ else
+ parag->paint(*p, pal, 0, false);
+ p->translate(0, -y);
+ parag = parag->next();
+ if (!flow()->isEmpty())
+ flow()->drawFloatingItems(p, rect.x(), rect.y(), rect.width(), rect.height(), pal, false);
+ }
+ Q3TextFormat::setPainter(oldPainter);
+}
+
+void Q3TextDocument::drawParagraph(QPainter *painter, Q3TextParagraph *parag, int cx, int cy,
+ int cw, int ch,
+ QPixmap *&/*doubleBuffer*/, const QPalette &pal,
+ bool drawCursor, Q3TextCursor *cursor, bool resetChanged)
+{
+ if (resetChanged)
+ parag->setChanged(false);
+ QRect ir(parag->rect());
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!parag->tableCell())
+#endif
+ ir.setWidth(width());
+
+ painter->translate(ir.x(), ir.y());
+
+ if (!parag->document()->parent()) {
+ const QPoint oldOrigin = painter->brushOrigin();
+ painter->setBrushOrigin(-ir.topLeft());
+ painter->fillRect(QRect(0, 0, ir.width(), ir.height()), parag->backgroundBrush(pal));
+ painter->setBrushOrigin(oldOrigin);
+ }
+
+ painter->translate(-(ir.x() - parag->rect().x()),
+ -(ir.y() - parag->rect().y()));
+ parag->paint(*painter, pal, drawCursor ? cursor : 0, true, cx, cy, cw, ch);
+
+ painter->translate(-ir.x(), -ir.y());
+
+ parag->document()->nextDoubleBuffered = false;
+}
+
+Q3TextParagraph *Q3TextDocument::draw(QPainter *p, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool onlyChanged, bool drawCursor,
+ Q3TextCursor *cursor, bool resetChanged)
+{
+ if (withoutDoubleBuffer || (par && par->withoutDoubleBuffer)) {
+ withoutDoubleBuffer = true;
+ QRect r;
+ draw(p, r, pal);
+ return 0;
+ }
+ withoutDoubleBuffer = false;
+
+ if (!firstParagraph())
+ return 0;
+
+ QPainter * oldPainter = Q3TextFormat::painter();
+ Q3TextFormat::setPainter(p);
+ if (formatCollection()->defaultFormat()->color() != pal.text().color())
+ setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color());
+
+ if (cx < 0 && cy < 0) {
+ cx = 0;
+ cy = 0;
+ cw = width();
+ ch = height();
+ }
+
+ Q3TextParagraph *lastFormatted = 0;
+ Q3TextParagraph *parag = firstParagraph();
+
+ QPixmap *doubleBuffer = 0;
+
+ while (parag) {
+ lastFormatted = parag;
+ if (!parag->isValid())
+ parag->format();
+
+ QRect pr = parag->rect();
+ pr.setWidth(parag->document()->width());
+ if (pr.y() > cy + ch)
+ goto floating;
+ QRect clipr(cx, cy, cw, ch);
+ if (!pr.intersects(clipr) || (onlyChanged && !parag->hasChanged())) {
+ pr.setWidth(parag->document()->width());
+ parag = parag->next();
+ continue;
+ }
+
+ drawParagraph(p, parag, cx, cy, cw, ch, doubleBuffer, pal, drawCursor,
+ cursor, resetChanged);
+ parag = parag->next();
+ }
+
+ parag = lastParagraph();
+
+ floating:
+ if (parag->rect().y() + parag->rect().height() < parag->document()->height()) {
+ if (!parag->document()->parent()) {
+ QRect fillRect = QRect(0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
+ parag->document()->height() - (parag->rect().y() + parag->rect().height()));
+ if (QRect(cx, cy, cw, ch).intersects(fillRect))
+ p->fillRect(fillRect, pal.brush(QPalette::Base));
+ }
+ if (!flow()->isEmpty()) {
+ QRect cr(cx, cy, cw, ch);
+ flow()->drawFloatingItems(p, cr.x(), cr.y(), cr.width(), cr.height(), pal, false);
+ }
+ }
+
+ if (buf_pixmap && buf_pixmap->height() > 300) {
+ delete buf_pixmap;
+ buf_pixmap = 0;
+ }
+
+ Q3TextFormat::setPainter(oldPainter);
+ return lastFormatted;
+}
+
+/*
+ #### this function only sets the default font size in the format collection
+ */
+void Q3TextDocument::setDefaultFormat(const QFont &font, const QColor &color)
+{
+ bool reformat = font != fCollection->defaultFormat()->font();
+ for (int idx = 0; idx < childList.size(); ++idx) {
+ Q3TextDocument *dc = childList.at(idx);
+ dc->setDefaultFormat(font, color);
+ }
+ fCollection->updateDefaultFormat(font, color, sheet_);
+
+ if (!reformat)
+ return;
+ tStopWidth = formatCollection()->defaultFormat()->width(QLatin1Char('x')) * 8;
+
+ // invalidate paragraphs and custom items
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ p->invalidate(0);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int i = 0; i < p->length() - 1; ++i)
+ if (p->at(i)->isCustom())
+ p->at(i)->customItem()->invalidate();
+#endif
+ p = p->next();
+ }
+}
+
+
+/*!
+ \preliminary
+
+ Generates an internal object for the tag called \a name, given the
+ attributes \a attr, and using additional information provided by
+ the mime source factory \a factory.
+
+ \a context is the optional context of the document, i.e. the path
+ to look for relative links. This becomes important if the text
+ contains relative references, for example within image tags.
+ QSimpleRichText always uses the default mime source factory (see
+ \l{Q3MimeSourceFactory::defaultFactory()}) to resolve these
+ references. The context will then be used to calculate the
+ absolute path. See Q3MimeSourceFactory::makeAbsolute() for details.
+
+ \a emptyTag and \a doc are for internal use only.
+
+ This function should not be used in application code.
+*/
+#ifndef QT_NO_TEXTCUSTOMITEM
+Q3TextCustomItem* Q3TextDocument::tag(Q3StyleSheet *sheet, const QString& name,
+ const QMap<QString, QString> &attr,
+ const QString& context,
+ const Q3MimeSourceFactory& factory,
+ bool /*emptyTag */, Q3TextDocument *doc)
+{
+ const Q3StyleSheetItem* style = sheet->item(name);
+ // first some known tags
+ if (!style)
+ return 0;
+ if (style->name() == QLatin1String("img"))
+ return new Q3TextImage(doc, attr, context, (Q3MimeSourceFactory&)factory);
+ if (style->name() == QLatin1String("hr"))
+ return new Q3TextHorizontalLine(doc, attr, context, (Q3MimeSourceFactory&)factory );
+ return 0;
+}
+#endif
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextDocument::registerCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p)
+{
+ if (i && i->placement() != Q3TextCustomItem::PlaceInline) {
+ flow_->registerFloatingItem(i);
+ p->registerFloatingItem(i);
+ i->setParagraph(p);
+ }
+ p->mightHaveCustomItems = mightHaveCustomItems = true;
+}
+
+void Q3TextDocument::unregisterCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p)
+{
+ p->unregisterFloatingItem(i);
+ i->setParagraph(0);
+ flow_->unregisterFloatingItem(i);
+}
+#endif
+
+bool Q3TextDocument::hasFocusParagraph() const
+{
+ return !!focusIndicator.parag;
+}
+
+QString Q3TextDocument::focusHref() const
+{
+ return focusIndicator.href;
+}
+
+QString Q3TextDocument::focusName() const
+{
+ return focusIndicator.name;
+}
+
+bool Q3TextDocument::focusNextPrevChild(bool next)
+{
+ if (!focusIndicator.parag) {
+ if (next) {
+ focusIndicator.parag = fParag;
+ focusIndicator.start = 0;
+ focusIndicator.len = 0;
+ } else {
+ focusIndicator.parag = lParag;
+ focusIndicator.start = lParag->length();
+ focusIndicator.len = 0;
+ }
+ } else {
+ focusIndicator.parag->setChanged(true);
+ }
+ focusIndicator.href.clear();
+ focusIndicator.name.clear();
+
+ if (next) {
+ Q3TextParagraph *p = focusIndicator.parag;
+ int index = focusIndicator.start + focusIndicator.len;
+ while (p) {
+ for (int i = index; i < p->length(); ++i) {
+ if (p->at(i)->isAnchor()) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = p->at(i)->anchorHref();
+ focusIndicator.name = p->at(i)->anchorName();
+ while (i < p->length()) {
+ if (!p->at(i)->isAnchor())
+ return true;
+ focusIndicator.len++;
+ i++;
+ }
+#ifndef QT_NO_TEXTCUSTOMITEM
+ } else if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ // first try to continue
+ int idx;
+ bool resetCells = true;
+ for (idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (c->richText()->hasFocusParagraph()) {
+ if (c->richText()->focusNextPrevChild(next)) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ } else {
+ resetCells = false;
+ ++idx;
+ break;
+ }
+ }
+ }
+ // now really try
+ if (resetCells)
+ idx = 0;
+ for (; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (c->richText()->focusNextPrevChild(next)) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ }
+ }
+ }
+#endif
+ }
+ }
+ index = 0;
+ p = p->next();
+ }
+ } else {
+ Q3TextParagraph *p = focusIndicator.parag;
+ int index = focusIndicator.start - 1;
+ if (focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1)
+ index++;
+ while (p) {
+ for (int i = index; i >= 0; --i) {
+ if (p->at(i)->isAnchor()) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = p->at(i)->anchorHref();
+ focusIndicator.name = p->at(i)->anchorName();
+ while (i >= -1) {
+ if (i < 0 || !p->at(i)->isAnchor()) {
+ focusIndicator.start++;
+ return true;
+ }
+ if (i < 0)
+ break;
+ focusIndicator.len++;
+ focusIndicator.start--;
+ i--;
+ }
+#ifndef QT_NO_TEXTCUSTOMITEM
+ } else if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ // first try to continue
+ int idx;
+ bool resetCells = true;
+ for (idx = cells.size()-1; idx >= 0; --idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (c->richText()->hasFocusParagraph()) {
+ if (c->richText()->focusNextPrevChild(next)) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ } else {
+ resetCells = false;
+ --idx;
+ break;
+ }
+ }
+ }
+ // now really try
+ if (resetCells)
+ idx = cells.size()-1;
+ for (; idx >= 0; --idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (c->richText()->focusNextPrevChild(next)) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ }
+ }
+ }
+#endif
+ }
+ }
+ p = p->prev();
+ if (p)
+ index = p->length() - 1;
+ }
+ }
+
+ focusIndicator.parag = 0;
+
+ return false;
+}
+
+int Q3TextDocument::length() const
+{
+ int l = -1;
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ l += p->length();
+ p = p->next();
+ }
+ return qMax(0,l);
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+int Q3TextFormat::width(const QChar &c) const
+{
+ if (c.unicode() == 0xad) // soft hyphen
+ return 0;
+ if (!pntr || !pntr->isActive()) {
+ if (c == QLatin1Char('\t'))
+ return fm.width(QLatin1Char(' '));
+ if (ha == AlignNormal) {
+ int w;
+ if (c.row())
+ w = fm.width(c);
+ else
+ w = widths[c.unicode()];
+ if (w == 0 && !c.row()) {
+ w = fm.width(c);
+ ((Q3TextFormat*)this)->widths[c.unicode()] = w;
+ }
+ return w;
+ } else {
+ QFont f(fn);
+ if (usePixelSizes)
+ f.setPixelSize((f.pixelSize() * 2) / 3);
+ else
+ f.setPointSize((f.pointSize() * 2) / 3);
+ QFontMetrics fm_(f);
+ return fm_.width(c);
+ }
+ }
+
+ QFont f(fn);
+ if (ha != AlignNormal) {
+ if (usePixelSizes)
+ f.setPixelSize((f.pixelSize() * 2) / 3);
+ else
+ f.setPointSize((f.pointSize() * 2) / 3);
+ }
+ applyFont(f);
+
+ return pntr_fm->width(c);
+}
+
+int Q3TextFormat::width(const QString &str, int pos) const
+{
+ int w = 0;
+ if (str.unicode()[pos].unicode() == 0xad)
+ return w;
+ if (!pntr || !pntr->isActive()) {
+ if (ha == AlignNormal) {
+ w = fm.charWidth(str, pos);
+ } else {
+ QFont f(fn);
+ if (usePixelSizes)
+ f.setPixelSize((f.pixelSize() * 2) / 3);
+ else
+ f.setPointSize((f.pointSize() * 2) / 3);
+ QFontMetrics fm_(f);
+ w = fm_.charWidth(str, pos);
+ }
+ } else {
+ QFont f(fn);
+ if (ha != AlignNormal) {
+ if (usePixelSizes)
+ f.setPixelSize((f.pixelSize() * 2) / 3);
+ else
+ f.setPointSize((f.pointSize() * 2) / 3);
+ }
+ applyFont(f);
+ w = pntr_fm->charWidth(str, pos);
+ }
+ return w;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextString::Q3TextString()
+{
+ bidiDirty = true;
+ bidi = false;
+ rightToLeft = false;
+ dir = QChar::DirON;
+}
+
+Q3TextString::Q3TextString(const Q3TextString &s)
+{
+ bidiDirty = true;
+ bidi = s.bidi;
+ rightToLeft = s.rightToLeft;
+ dir = s.dir;
+ data = s.data;
+ data.detach();
+ for (int i = 0; i < (int)data.size(); ++i) {
+ Q3TextFormat *f = data[i].format();
+ if (f)
+ f->addRef();
+ }
+}
+
+void Q3TextString::insert(int index, const QString &s, Q3TextFormat *f)
+{
+ insert(index, s.unicode(), s.length(), f);
+}
+
+void Q3TextString::insert(int index, const QChar *unicode, int len, Q3TextFormat *f)
+{
+ int os = data.size();
+ data.resize(data.size() + len);
+ if (index < os) {
+ memmove(data.data() + index + len, data.data() + index,
+ sizeof(Q3TextStringChar) * (os - index));
+ }
+ Q3TextStringChar *ch = data.data() + index;
+ for (int i = 0; i < len; ++i) {
+ ch->x = 0;
+ ch->lineStart = 0;
+ ch->nobreak = false;
+ ch->type = Q3TextStringChar::Regular;
+ ch->p.format = f;
+ ch->rightToLeft = 0;
+ ch->c = unicode[i];
+ ++ch;
+ }
+ bidiDirty = true;
+}
+
+Q3TextString::~Q3TextString()
+{
+ clear();
+}
+
+void Q3TextString::insert(int index, Q3TextStringChar *c, bool doAddRefFormat )
+{
+ int os = data.size();
+ data.resize(data.size() + 1);
+ if (index < os) {
+ memmove(data.data() + index + 1, data.data() + index,
+ sizeof(Q3TextStringChar) * (os - index));
+ }
+ Q3TextStringChar &ch = data[(int)index];
+ ch.c = c->c;
+ ch.x = 0;
+ ch.lineStart = 0;
+ ch.rightToLeft = 0;
+ ch.p.format = 0;
+ ch.type = Q3TextStringChar::Regular;
+ ch.nobreak = false;
+ if (doAddRefFormat && c->format())
+ c->format()->addRef();
+ ch.setFormat(c->format());
+ bidiDirty = true;
+}
+
+int Q3TextString::appendParagraphs( Q3TextParagraph *start, Q3TextParagraph *end )
+{
+ int paragCount = 0;
+ int newLength = data.size();
+ for (Q3TextParagraph *p = start; p != end; p = p->next()) {
+ newLength += p->length();
+ ++paragCount;
+ }
+
+ const int oldLength = data.size();
+ data.resize(newLength);
+
+ Q3TextStringChar *d = &data[oldLength];
+ for (Q3TextParagraph *p = start; p != end; p = p->next()) {
+ const Q3TextStringChar * const src = p->at(0);
+ int i = 0;
+ for (; i < p->length() - 1; ++i) {
+ d[i].c = src[i].c;
+ d[i].x = 0;
+ d[i].lineStart = 0;
+ d[i].rightToLeft = 0;
+ d[i].type = Q3TextStringChar::Regular;
+ d[i].nobreak = false;
+ d[i].p.format = src[i].format();
+ if (d[i].p.format)
+ d[i].p.format->addRef();
+ }
+ d[i].x = 0;
+ d[i].lineStart = 0;
+ d[i].nobreak = false;
+ d[i].type = Q3TextStringChar::Regular;
+ d[i].p.format = 0;
+ d[i].rightToLeft = 0;
+ d[i].c = QLatin1Char('\n');
+ d += p->length();
+ }
+
+ bidiDirty = true;
+ return paragCount;
+}
+
+void Q3TextString::truncate(int index)
+{
+ index = qMax(index, 0);
+ index = qMin(index, (int)data.size() - 1);
+ if (index < (int)data.size()) {
+ for (int i = index + 1; i < (int)data.size(); ++i) {
+ Q3TextStringChar &ch = data[i];
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!(ch.type == Q3TextStringChar::Regular)) {
+ delete ch.customItem();
+ if (ch.p.custom->format)
+ ch.p.custom->format->removeRef();
+ delete ch.p.custom;
+ ch.p.custom = 0;
+ } else
+#endif
+ if (ch.format()) {
+ ch.format()->removeRef();
+ }
+ }
+ }
+ data.resize(index);
+ bidiDirty = true;
+}
+
+void Q3TextString::remove(int index, int len)
+{
+ for (int i = index; i < (int)data.size() && i - index < len; ++i) {
+ Q3TextStringChar &ch = data[i];
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!(ch.type == Q3TextStringChar::Regular)) {
+ delete ch.customItem();
+ if (ch.p.custom->format)
+ ch.p.custom->format->removeRef();
+ delete ch.p.custom;
+ ch.p.custom = 0;
+ } else
+#endif
+ if (ch.format()) {
+ ch.format()->removeRef();
+ }
+ }
+ memmove(data.data() + index, data.data() + index + len,
+ sizeof(Q3TextStringChar) * (data.size() - index - len));
+ data.resize(data.size() - len);
+ bidiDirty = true;
+}
+
+void Q3TextString::clear()
+{
+ for (int i = 0; i < (int)data.count(); ++i) {
+ Q3TextStringChar &ch = data[i];
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!(ch.type == Q3TextStringChar::Regular)) {
+ if (ch.customItem() && ch.customItem()->placement() == Q3TextCustomItem::PlaceInline)
+ delete ch.customItem();
+ if (ch.p.custom->format)
+ ch.p.custom->format->removeRef();
+ delete ch.p.custom;
+ ch.p.custom = 0;
+ } else
+#endif
+ if (ch.format()) {
+ ch.format()->removeRef();
+ }
+ }
+ data.resize(0);
+ bidiDirty = true;
+}
+
+void Q3TextString::setFormat(int index, Q3TextFormat *f, bool useCollection)
+{
+ Q3TextStringChar &ch = data[index];
+ if (useCollection && ch.format())
+ ch.format()->removeRef();
+ ch.setFormat(f);
+}
+
+void Q3TextString::checkBidi() const
+{
+ // ############ fix BIDI handling
+ Q3TextString *that = (Q3TextString *)this;
+ that->bidiDirty = false;
+ int length = data.size();
+ if (!length) {
+ that->bidi = rightToLeft;
+ that->rightToLeft = (dir == QChar::DirR);
+ return;
+ }
+
+ if (dir == QChar::DirR) {
+ that->rightToLeft = true;
+ } else if (dir == QChar::DirL) {
+ that->rightToLeft = false;
+ } else {
+ that->rightToLeft = (QApplication::layoutDirection() == Qt::RightToLeft);
+ }
+
+ const Q3TextStringChar *start = data.data();
+ const Q3TextStringChar *end = start + length;
+
+ ((Q3TextString *)this)->stringCache = toString(data);
+
+ // determines the properties we need for layouting
+ QTextEngine textEngine;
+ textEngine.text = toString();
+ textEngine.option.setTextDirection(rightToLeft ? Qt::RightToLeft : Qt::LeftToRight);
+ textEngine.itemize();
+ const HB_CharAttributes *ca = textEngine.attributes() + length-1;
+ Q3TextStringChar *ch = (Q3TextStringChar *)end - 1;
+ QScriptItem *item = &textEngine.layoutData->items[textEngine.layoutData->items.size()-1];
+ unsigned char bidiLevel = item->analysis.bidiLevel;
+ that->bidi = (bidiLevel || rightToLeft);
+ int pos = length-1;
+ while (ch >= start) {
+ if (item->position > pos) {
+ --item;
+ Q_ASSERT(item >= &textEngine.layoutData->items[0]);
+ bidiLevel = item->analysis.bidiLevel;
+ if (bidiLevel)
+ that->bidi = true;
+ }
+ ch->softBreak = ca->lineBreakType >= HB_Break;
+ ch->whiteSpace = ca->whiteSpace;
+ ch->charStop = ca->charStop;
+ ch->bidiLevel = bidiLevel;
+ ch->rightToLeft = (bidiLevel%2);
+ --ch;
+ --ca;
+ --pos;
+ }
+}
+
+void Q3TextDocument::setStyleSheet(Q3StyleSheet *s)
+{
+ if (!s)
+ return;
+ sheet_ = s;
+ list_tm = list_bm = par_tm = par_bm = 12;
+ list_lm = 40;
+ li_tm = li_bm = 0;
+ Q3StyleSheetItem* item = s->item(QLatin1String("ol"));
+ if (item) {
+ list_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
+ list_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
+ list_lm = qMax(0,item->margin(Q3StyleSheetItem::MarginLeft));
+ }
+ if ((item = s->item(QLatin1String("li")))) {
+ li_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
+ li_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
+ }
+ if ((item = s->item(QLatin1String("p")))) {
+ par_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
+ par_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
+ }
+}
+
+void Q3TextDocument::setUnderlineLinks(bool b) {
+ underlLinks = b;
+ for (int idx = 0; idx < childList.size(); ++idx) {
+ Q3TextDocument *dc = childList.at(idx);
+ dc->setUnderlineLinks(b);
+ }
+}
+
+void Q3TextStringChar::setFormat(Q3TextFormat *f)
+{
+ if (type == Regular) {
+ p.format = f;
+ } else {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!p.custom) {
+ p.custom = new CustomData;
+ p.custom->custom = 0;
+ }
+ p.custom->format = f;
+#endif
+ }
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextStringChar::setCustomItem(Q3TextCustomItem *i)
+{
+ if (type == Regular) {
+ Q3TextFormat *f = format();
+ p.custom = new CustomData;
+ p.custom->format = f;
+ } else {
+ delete p.custom->custom;
+ }
+ p.custom->custom = i;
+ type = (type == Anchor ? CustomAnchor : Custom);
+}
+
+void Q3TextStringChar::loseCustomItem()
+{
+ if (type == Custom) {
+ Q3TextFormat *f = p.custom->format;
+ p.custom->custom = 0;
+ delete p.custom;
+ type = Regular;
+ p.format = f;
+ } else if (type == CustomAnchor) {
+ p.custom->custom = 0;
+ type = Anchor;
+ }
+}
+
+#endif
+
+QString Q3TextStringChar::anchorName() const
+{
+ if (type == Regular)
+ return QString();
+ else
+ return p.custom->anchorName;
+}
+
+QString Q3TextStringChar::anchorHref() const
+{
+ if (type == Regular)
+ return QString();
+ else
+ return p.custom->anchorHref;
+}
+
+void Q3TextStringChar::setAnchor(const QString& name, const QString& href)
+{
+ if (type == Regular) {
+ Q3TextFormat *f = format();
+ p.custom = new CustomData;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ p.custom->custom = 0;
+#endif
+ p.custom->format = f;
+ type = Anchor;
+ } else if (type == Custom) {
+ type = CustomAnchor;
+ }
+ p.custom->anchorName = name;
+ p.custom->anchorHref = href;
+}
+
+
+int Q3TextString::width(int idx) const
+{
+ int w = 0;
+ Q3TextStringChar *c = &at(idx);
+ if (!c->charStop || c->c.unicode() == 0xad || c->c.unicode() == 0x2028)
+ return 0;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if(c->isCustom()) {
+ if(c->customItem()->placement() == Q3TextCustomItem::PlaceInline)
+ w = c->customItem()->width;
+ } else
+#endif
+ {
+ int r = c->c.row();
+ if(r < 0x06
+#ifndef Q_WS_WIN
+ // Uniscribe's handling of Asian makes the condition below fail.
+ || (r > 0x1f && !(r > 0xd7 && r < 0xe0))
+#endif
+ ) {
+ w = c->format()->width(c->c);
+ } else {
+ // complex text. We need some hacks to get the right metric here
+ w = c->format()->width(toString(), idx);
+ }
+ }
+ return w;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextParagraph::Q3TextParagraph(Q3TextDocument *dc, Q3TextParagraph *pr, Q3TextParagraph *nx, bool updateIds)
+ : p(pr), n(nx), docOrPseudo(dc),
+ changed(false), firstFormat(true), firstPProcess(true), needPreProcess(false), fullWidth(true),
+ lastInFrame(false), visible(true), breakable(true), movedDown(false),
+ mightHaveCustomItems(false), hasdoc(dc != 0), litem(false), rtext(false),
+ align(0), lstyle(Q3StyleSheetItem::ListDisc), invalid(0), mSelections(0),
+#ifndef QT_NO_TEXTCUSTOMITEM
+ mFloatingItems(0),
+#endif
+ utm(0), ubm(0), ulm(0), urm(0), uflm(0), ulinespacing(0),
+ tabStopWidth(0), minwidth(0), tArray(0), eData(0), ldepth(0)
+{
+ lstyle = Q3StyleSheetItem::ListDisc;
+ if (!hasdoc)
+ docOrPseudo = new Q3TextParagraphPseudoDocument;
+ bgcol = 0;
+ list_val = -1;
+ paintdevice = 0;
+ Q3TextFormat* defFormat = formatCollection()->defaultFormat();
+ if (!hasdoc) {
+ tabStopWidth = defFormat->width(QLatin1Char('x')) * 8;
+ pseudoDocument()->commandHistory = new Q3TextCommandHistory(100);
+ }
+
+ if (p)
+ p->n = this;
+ if (n)
+ n->p = this;
+
+ if (!p && hasdoc)
+ document()->setFirstParagraph(this);
+ if (!n && hasdoc)
+ document()->setLastParagraph(this);
+
+ state = -1;
+
+ if (p)
+ id = p->id + 1;
+ else
+ id = 0;
+ if (n && updateIds) {
+ Q3TextParagraph *s = n;
+ while (s) {
+ s->id = s->p->id + 1;
+ s->invalidateStyleCache();
+ s = s->n;
+ }
+ }
+
+ str = new Q3TextString();
+ const QChar ch(QLatin1Char(' '));
+ str->insert(0, &ch, 1, formatCollection()->defaultFormat());
+}
+
+Q3TextParagraph::~Q3TextParagraph()
+{
+ delete str;
+ if (hasdoc) {
+ register Q3TextDocument *doc = document();
+ if (this == doc->minwParag) {
+ doc->minwParag = 0;
+ doc->minw = 0;
+ }
+ if (this == doc->curParag)
+ doc->curParag = 0;
+ } else {
+ delete pseudoDocument();
+ }
+ delete [] tArray;
+ delete eData;
+ QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
+ for (; it != lineStarts.end(); ++it)
+ delete *it;
+ if (mSelections)
+ delete mSelections;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (mFloatingItems)
+ delete mFloatingItems;
+#endif
+ if (p)
+ p->setNext(n);
+ if (n)
+ n->setPrev(p);
+ delete bgcol;
+}
+
+void Q3TextParagraph::setNext(Q3TextParagraph *s)
+{
+ n = s;
+ if (!n && hasdoc)
+ document()->setLastParagraph(this);
+}
+
+void Q3TextParagraph::setPrev(Q3TextParagraph *s)
+{
+ p = s;
+ if (!p && hasdoc)
+ document()->setFirstParagraph(this);
+}
+
+void Q3TextParagraph::invalidate(int chr)
+{
+ if (invalid < 0)
+ invalid = chr;
+ else
+ invalid = qMin(invalid, chr);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (mFloatingItems) {
+ for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
+ Q3TextCustomItem *i = mFloatingItems->at(idx);
+ i->ypos = -1;
+ }
+ }
+#endif
+ invalidateStyleCache();
+}
+
+void Q3TextParagraph::invalidateStyleCache()
+{
+ if (list_val < 0)
+ list_val = -1;
+}
+
+
+void Q3TextParagraph::insert(int index, const QString &s)
+{
+ insert(index, s.unicode(), s.length());
+}
+
+void Q3TextParagraph::insert(int index, const QChar *unicode, int len)
+{
+ if (hasdoc && !document()->useFormatCollection() && document()->preProcessor())
+ str->insert(index, unicode, len,
+ document()->preProcessor()->format(Q3TextPreProcessor::Standard));
+ else
+ str->insert(index, unicode, len, formatCollection()->defaultFormat());
+ invalidate(index);
+ needPreProcess = true;
+}
+
+void Q3TextParagraph::truncate(int index)
+{
+ str->truncate(index);
+ insert(length(), QLatin1String(" "));
+ needPreProcess = true;
+}
+
+void Q3TextParagraph::remove(int index, int len)
+{
+ if (index + len - str->length() > 0)
+ return;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int i = index; i < index + len; ++i) {
+ Q3TextStringChar *c = at(i);
+ if (hasdoc && c->isCustom()) {
+ document()->unregisterCustomItem(c->customItem(), this);
+ }
+ }
+#endif
+ str->remove(index, len);
+ invalidate(0);
+ needPreProcess = true;
+}
+
+void Q3TextParagraph::join(Q3TextParagraph *s)
+{
+ int oh = r.height() + s->r.height();
+ n = s->n;
+ if (n)
+ n->p = this;
+ else if (hasdoc)
+ document()->setLastParagraph(this);
+
+ int start = str->length();
+ if (length() > 0 && at(length() - 1)->c == QLatin1Char(' ')) {
+ remove(length() - 1, 1);
+ --start;
+ }
+ append(s->str->toString(), true);
+
+ for (int i = 0; i < s->length(); ++i) {
+ if (!hasdoc || document()->useFormatCollection()) {
+ s->str->at(i).format()->addRef();
+ str->setFormat(i + start, s->str->at(i).format(), true);
+ }
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (s->str->at(i).isCustom()) {
+ Q3TextCustomItem * item = s->str->at(i).customItem();
+ str->at(i + start).setCustomItem(item);
+ s->str->at(i).loseCustomItem();
+ if (hasdoc) {
+ document()->unregisterCustomItem(item, s);
+ document()->registerCustomItem(item, this);
+ }
+ }
+ if (s->str->at(i).isAnchor()) {
+ str->at(i + start).setAnchor(s->str->at(i).anchorName(),
+ s->str->at(i).anchorHref());
+ }
+#endif
+ }
+
+ if (!extraData() && s->extraData()) {
+ setExtraData(s->extraData());
+ s->setExtraData(0);
+ } else if (extraData() && s->extraData()) {
+ extraData()->join(s->extraData());
+ }
+ delete s;
+ invalidate(0);
+ r.setHeight(oh);
+ needPreProcess = true;
+ if (n) {
+ Q3TextParagraph *s = n;
+ s->invalidate(0);
+ while (s) {
+ s->id = s->p->id + 1;
+ s->state = -1;
+ s->needPreProcess = true;
+ s->changed = true;
+ s->invalidateStyleCache();
+ s = s->n;
+ }
+ }
+ format();
+ state = -1;
+}
+
+void Q3TextParagraph::move(int &dy)
+{
+ if (dy == 0)
+ return;
+ changed = true;
+ r.moveBy(0, dy);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (mFloatingItems) {
+ for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
+ Q3TextCustomItem *i = mFloatingItems->at(idx);
+ i->ypos += dy;
+ }
+ }
+#endif
+ if (p)
+ p->lastInFrame = true;
+
+ // do page breaks if required
+ if (hasdoc && document()->isPageBreakEnabled()) {
+ int shift;
+ if ((shift = document()->formatter()->formatVertically( document(), this))) {
+ if (p)
+ p->setChanged(true);
+ dy += shift;
+ }
+ }
+}
+
+void Q3TextParagraph::format(int start, bool doMove)
+{
+ if (!str || str->length() == 0 || !formatter())
+ return;
+
+ if (hasdoc &&
+ document()->preProcessor() &&
+ (needPreProcess || state == -1))
+ document()->preProcessor()->process(document(), this, invalid <= 0 ? 0 : invalid);
+ needPreProcess = false;
+
+ if (invalid == -1)
+ return;
+
+ r.moveTopLeft(QPoint(documentX(), p ? p->r.y() + p->r.height() : documentY()));
+ if (p)
+ p->lastInFrame = false;
+
+ movedDown = false;
+ bool formattedAgain = false;
+
+ formatAgain:
+
+ r.setWidth(documentWidth());
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (hasdoc && mFloatingItems) {
+ for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
+ Q3TextCustomItem *i = mFloatingItems->at(idx);
+ i->ypos = r.y();
+ if (i->placement() == Q3TextCustomItem::PlaceRight) {
+ i->xpos = r.x() + r.width() - i->width;
+ }
+ }
+ }
+#endif
+ QMap<int, QTextLineStart*> oldLineStarts = lineStarts;
+ lineStarts.clear();
+ int y = formatter()->format(document(), this, start, oldLineStarts);
+
+
+ r.setWidth(qMax(r.width(), formatter()->minimumWidth()));
+
+
+ QMap<int, QTextLineStart*>::Iterator it = oldLineStarts.begin();
+
+ for (; it != oldLineStarts.end(); ++it)
+ delete *it;
+
+ if (!hasdoc) { // qt_format_text bounding rect handling
+ it = lineStarts.begin();
+ int usedw = 0;
+ for (; it != lineStarts.end(); ++it)
+ usedw = qMax(usedw, (*it)->w);
+ if (r.width() <= 0) {
+ // if the user specifies an invalid rect, this means that the
+ // bounding box should grow to the width that the text actually
+ // needs
+ r.setWidth(usedw);
+ } else {
+ r.setWidth(qMin(usedw, r.width()));
+ }
+ }
+
+ if (y != r.height())
+ r.setHeight(y);
+
+ if (!visible) {
+ r.setHeight(0);
+ } else {
+ int minw = minwidth = formatter()->minimumWidth();
+ int wused = formatter()->widthUsed();
+ wused = qMax(minw, wused);
+ if (hasdoc) {
+ document()->setMinimumWidth(minw, wused, this);
+ } else {
+ pseudoDocument()->minw = qMax(pseudoDocument()->minw, minw);
+ pseudoDocument()->wused = qMax(pseudoDocument()->wused, wused);
+ }
+ }
+
+ // do page breaks if required
+ if (hasdoc && document()->isPageBreakEnabled()) {
+ int shift = document()->formatter()->formatVertically(document(), this);
+ if (shift && !formattedAgain) {
+ formattedAgain = true;
+ goto formatAgain;
+ }
+ }
+
+ if (n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y()) {
+ int dy = (r.y() + r.height()) - n->r.y();
+ Q3TextParagraph *s = n;
+ bool makeInvalid = p && p->lastInFrame;
+ while (s && dy) {
+ if (!s->isFullWidth())
+ makeInvalid = true;
+ if (makeInvalid)
+ s->invalidate(0);
+ s->move(dy);
+ if (s->lastInFrame)
+ makeInvalid = true;
+ s = s->n;
+ }
+ }
+
+ firstFormat = false;
+ changed = true;
+ invalid = -1;
+ //##### string()->setTextChanged(false);
+}
+
+int Q3TextParagraph::lineHeightOfChar(int i, int *bl, int *y) const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
+ --it;
+ for (;;) {
+ if (i >= it.key()) {
+ if (bl)
+ *bl = (*it)->baseLine;
+ if (y)
+ *y = (*it)->y;
+ return (*it)->h;
+ }
+ if (it == lineStarts.begin())
+ break;
+ --it;
+ }
+
+ qWarning("Q3TextParagraph::lineHeightOfChar: couldn't find lh for %d", i);
+ return 15;
+}
+
+Q3TextStringChar *Q3TextParagraph::lineStartOfChar(int i, int *index, int *line) const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ int l = (int)lineStarts.count() - 1;
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
+ --it;
+ for (;;) {
+ if (i >= it.key()) {
+ if (index)
+ *index = it.key();
+ if (line)
+ *line = l;
+ return &str->at(it.key());
+ }
+ if (it == lineStarts.begin())
+ break;
+ --it;
+ --l;
+ }
+
+ qWarning("Q3TextParagraph::lineStartOfChar: couldn't find %d", i);
+ return 0;
+}
+
+int Q3TextParagraph::lines() const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ return (int)lineStarts.count();
+}
+
+Q3TextStringChar *Q3TextParagraph::lineStartOfLine(int line, int *index) const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ if (line >= 0 && line < (int)lineStarts.count()) {
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (line-- > 0)
+ ++it;
+ int i = it.key();
+ if (index)
+ *index = i;
+ return &str->at(i);
+ }
+
+ qWarning("Q3TextParagraph::lineStartOfLine: couldn't find %d", line);
+ return 0;
+}
+
+int Q3TextParagraph::leftGap() const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ if (str->length() == 0)
+ return 0;
+
+ int line = 0;
+ int x = str->length() ? str->at(0).x : 0; /* set x to x of first char */
+ if (str->isBidi()) {
+ for (int i = 1; i < str->length()-1; ++i)
+ x = qMin(x, str->at(i).x);
+ return x;
+ }
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (line < (int)lineStarts.count()) {
+ int i = it.key(); /* char index */
+ x = qMin(x, str->at(i).x);
+ ++it;
+ ++line;
+ }
+ return x;
+}
+
+void Q3TextParagraph::setFormat(int index, int len, Q3TextFormat *f, bool useCollection, int flags)
+{
+ if (!f)
+ return;
+ if (index < 0)
+ index = 0;
+ if (index > str->length() - 1)
+ index = str->length() - 1;
+ if (index + len >= str->length())
+ len = str->length() - index;
+
+ Q3TextFormatCollection *fc = 0;
+ if (useCollection)
+ fc = formatCollection();
+ Q3TextFormat *of;
+ for (int i = 0; i < len; ++i) {
+ of = str->at(i + index).format();
+ if (!changed && (!of || f->key() != of->key()))
+ changed = true;
+ if (invalid == -1 &&
+ (f->font().family() != of->font().family() ||
+ f->font().pointSize() != of->font().pointSize() ||
+ f->font().weight() != of->font().weight() ||
+ f->font().italic() != of->font().italic() ||
+ f->vAlign() != of->vAlign())) {
+ invalidate(0);
+ }
+ if (flags == -1 || flags == Q3TextFormat::Format || !fc) {
+ if (fc)
+ f = fc->format(f);
+ str->setFormat(i + index, f, useCollection);
+ } else {
+ Q3TextFormat *fm = fc->format(of, f, flags);
+ str->setFormat(i + index, fm, useCollection);
+ }
+ }
+}
+
+void Q3TextParagraph::indent(int *oldIndent, int *newIndent)
+{
+ if (!hasdoc || !document()->indent() || isListItem()) {
+ if (oldIndent)
+ *oldIndent = 0;
+ if (newIndent)
+ *newIndent = 0;
+ if (oldIndent && newIndent)
+ *newIndent = *oldIndent;
+ return;
+ }
+ document()->indent()->indent(document(), this, oldIndent, newIndent);
+}
+
+void Q3TextParagraph::paint(QPainter &painter, const QPalette &pal, Q3TextCursor *cursor,
+ bool drawSelections, int clipx, int clipy, int clipw, int cliph)
+{
+ if (!visible)
+ return;
+ int i, y, h, baseLine, xstart, xend = 0;
+ i = y =h = baseLine = 0;
+ QRect cursorRect;
+ drawSelections &= (mSelections != 0);
+ // macintosh full-width selection style
+ bool fullWidthStyle = QApplication::style()->styleHint(QStyle::SH_RichText_FullWidthSelection);
+ int fullSelectionWidth = 0;
+ if (drawSelections && fullWidthStyle)
+ fullSelectionWidth = (hasdoc ? document()->width() : r.width());
+
+ QString qstr = str->toString();
+ qstr.detach();
+ // ### workaround so that \n are not drawn, actually this should
+ // be fixed in QFont somewhere (under Windows you get ugly boxes
+ // otherwise)
+ QChar* uc = (QChar*) qstr.unicode();
+ for (int ii = 0; ii < qstr.length(); ii++)
+ if (uc[(int)ii]== QLatin1Char(QLatin1Char('\n')) || uc[(int)ii] == QLatin1Char('\t'))
+ uc[(int)ii] = 0x20;
+
+ int line = -1;
+ int paintStart = 0;
+ Q3TextStringChar *chr = 0;
+ Q3TextStringChar *nextchr = at(0);
+ for (i = 0; i < length(); i++) {
+ chr = nextchr;
+ if (i < length()-1)
+ nextchr = at(i+1);
+
+ // we flush at end of document
+ bool flush = (i == length()-1);
+ bool ignoreSoftHyphen = false;
+ if (!flush) {
+ // we flush at end of line
+ flush |= nextchr->lineStart;
+ // we flush on format changes
+ flush |= (nextchr->format() != chr->format());
+ // we flush on link changes
+ flush |= (nextchr->isLink() != chr->isLink());
+ // we flush on start of run
+ flush |= (nextchr->bidiLevel != chr->bidiLevel);
+ // we flush on bidi changes
+ flush |= (nextchr->rightToLeft != chr->rightToLeft);
+ // we flush before and after tabs
+ flush |= (chr->c == QLatin1Char('\t') || nextchr->c == QLatin1Char('\t'));
+ // we flush on soft hyphens
+ if (chr->c.unicode() == 0xad) {
+ flush = true;
+ if (!nextchr->lineStart)
+ ignoreSoftHyphen = true;
+ }
+ // we flush on custom items
+ flush |= chr->isCustom();
+ // we flush before custom items
+ flush |= nextchr->isCustom();
+ // when painting justified, we flush on spaces
+ if ((alignment() & Qt::AlignJustify) == Qt::AlignJustify)
+ flush |= chr->whiteSpace;
+ }
+
+ // init a new line
+ if (chr->lineStart) {
+ ++line;
+ paintStart = i;
+ lineInfo(line, y, h, baseLine);
+ if (clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph) { // outside clip area, leave
+ break;
+ }
+
+ // if this is the first line and we are a list item, draw the the bullet label
+ if (line == 0 && isListItem()) {
+ int x = chr->x;
+ if (str->isBidi()) {
+ if (str->isRightToLeft()) {
+ x = chr->x + str->width(0);
+ for (int k = 1; k < length(); ++k) {
+ if (str->at(k).lineStart)
+ break;
+ x = qMax(x, str->at(k).x + str->width(k));
+ }
+ } else {
+ x = chr->x;
+ for (int k = 1; k < length(); ++k) {
+ if (str->at(k).lineStart)
+ break;
+ x = qMin(x, str->at(k).x);
+ }
+ }
+ }
+ drawLabel(&painter, x, y, 0, 0, baseLine, pal);
+ }
+ }
+
+ // check for cursor mark
+ if (cursor && this == cursor->paragraph() && i == cursor->index()) {
+ Q3TextStringChar *c = i == 0 ? chr : chr - 1;
+ cursorRect.setRect(cursor->x() , y + baseLine - c->format()->ascent(),
+ 1, c->format()->height());
+ }
+
+ if (flush) { // something changed, draw what we have so far
+ if (chr->rightToLeft) {
+ xstart = chr->x;
+ xend = at(paintStart)->x + str->width(paintStart);
+ } else {
+ xstart = at(paintStart)->x;
+ xend = chr->x;
+ if (i < length() - 1) {
+ if (!str->at(i + 1).lineStart &&
+ str->at(i + 1).rightToLeft == chr->rightToLeft)
+ xend = str->at(i + 1).x;
+ else
+ xend += str->width(i);
+ }
+ }
+
+ if ((clipx == -1 || clipw <= 0 || (xend >= clipx && xstart <= clipx + clipw)) &&
+ (clipy == -1 || clipy < y+r.y()+h)) {
+ if (!chr->isCustom())
+ drawString(painter, qstr, paintStart, i - paintStart + (ignoreSoftHyphen ? 0 : 1), xstart, y,
+ baseLine, xend-xstart, h, drawSelections, fullSelectionWidth,
+ chr, pal, chr->rightToLeft);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ else if (chr->customItem()->placement() == Q3TextCustomItem::PlaceInline) {
+ bool inSelection = false;
+ if (drawSelections) {
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(Q3TextDocument::Standard);
+ inSelection = (it != mSelections->constEnd() && (*it).start <= i && (*it).end > i);
+ }
+ chr->customItem()->draw(&painter, chr->x, y,
+ clipx == -1 ? clipx : (clipx - r.x()),
+ clipy == -1 ? clipy : (clipy - r.y()),
+ clipw, cliph, pal, inSelection);
+ }
+#endif
+ }
+ paintStart = i+1;
+ }
+
+ }
+
+ // time to draw the cursor
+ const int cursor_extent = 4;
+ if (!cursorRect.isNull() && cursor &&
+ ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw))) {
+ painter.fillRect(cursorRect, pal.color(QPalette::Text));
+ painter.save();
+ if (string()->isBidi()) {
+ if (at(cursor->index())->rightToLeft) {
+ painter.setPen(Qt::black);
+ painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ } else {
+ painter.setPen(Qt::black);
+ painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ }
+ }
+ painter.restore();
+ }
+}
+
+//#define BIDI_DEBUG
+
+void Q3TextParagraph::setColorForSelection(QColor &color, QPainter &painter,
+ const QPalette &pal, int selection)
+{
+ if (selection < 0)
+ return;
+ color = (hasdoc && selection != Q3TextDocument::Standard) ?
+ document()->selectionColor(selection) :
+ pal.color(QPalette::Highlight);
+ QColor text = (hasdoc && document()->hasSelectionTextColor(selection)) ? document()->selectionTextColor(selection) : pal.color(QPalette::HighlightedText);
+ if (text.isValid())
+ painter.setPen(text);
+}
+
+void Q3TextParagraph::drawString(QPainter &painter, const QString &str, int start, int len,
+ int xstart, int y, int baseLine, int w, int h,
+ bool drawSelections, int fullSelectionWidth,
+ Q3TextStringChar *formatChar, const QPalette& pal,
+ bool rightToLeft)
+{
+ bool plainText = hasdoc ? document()->textFormat() == Qt::PlainText : false;
+ Q3TextFormat* format = formatChar->format();
+
+ int textFlags = int(rightToLeft ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
+
+ if (!plainText || (hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color()))
+ painter.setPen(QPen(format->color()));
+ else
+ painter.setPen(pal.text().color());
+ painter.setFont(format->font());
+
+ if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty()) {
+ if (format->useLinkColor())
+ painter.setPen(document()->linkColor.isValid() ? document()->linkColor :
+ pal.link().color());
+ if (document()->underlineLinks()) {
+ QFont fn = format->font();
+ fn.setUnderline(true);
+ painter.setFont(fn);
+ }
+ }
+
+ int real_length = len;
+ if (len && !rightToLeft && start + len == length()) // don't draw the last character (trailing space)
+ len--;
+ if (len && str.unicode()[start+len-1] == QChar::LineSeparator)
+ len--;
+
+
+ Q3TextFormat::VerticalAlignment vAlign = format->vAlign();
+ if (vAlign != Q3TextFormat::AlignNormal) {
+ // sub or superscript
+ QFont f(painter.font());
+ if (format->fontSizesInPixels())
+ f.setPixelSize((f.pixelSize() * 2) / 3);
+ else
+ f.setPointSize((f.pointSize() * 2) / 3);
+ painter.setFont(f);
+ int h = painter.fontMetrics().height();
+ baseLine += (vAlign == Q3TextFormat::AlignSubScript) ? h/6 : -h/2;
+ }
+
+ bool allSelected = false;
+ if (drawSelections) {
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(Q3TextDocument::Standard);
+ allSelected = (it != mSelections->constEnd() && (*it).start <= start && (*it).end >= start+len);
+ }
+ if (!allSelected)
+ painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0);
+
+#ifdef BIDI_DEBUG
+ painter.save();
+ painter.setPen (Qt::red);
+ painter.drawLine(xstart, y, xstart, y + baseLine);
+ painter.drawLine(xstart, y + baseLine/2, xstart + 10, y + baseLine/2);
+ int w = 0;
+ int i = 0;
+ while(i < len)
+ w += painter.fontMetrics().charWidth(str, start + i++);
+ painter.setPen (Qt::blue);
+ painter.drawLine(xstart + w - 1, y, xstart + w - 1, y + baseLine);
+ painter.drawLine(xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2);
+ painter.restore();
+#endif
+
+ // check if we are in a selection and draw it
+ if (drawSelections) {
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constEnd();
+ while (it != mSelections->constBegin()) {
+ --it;
+ int selStart = (*it).start;
+ int selEnd = (*it).end;
+ int tmpw = w;
+
+ selStart = qMax(selStart, start);
+ int real_selEnd = qMin(selEnd, start+real_length);
+ selEnd = qMin(selEnd, start+len);
+ bool extendRight = false;
+ bool extendLeft = false;
+ bool selWrap = (real_selEnd == length()-1 && n && n->hasSelection(it.key()));
+ if (selWrap
+ || ((real_selEnd < this->str->length()) && this->str->at(real_selEnd).lineStart)) {
+ extendRight = (fullSelectionWidth != 0);
+ if (!extendRight && !rightToLeft)
+ tmpw += painter.fontMetrics().width(QLatin1Char(' '));
+ }
+ if (fullSelectionWidth && (selStart == 0 || this->str->at(selStart).lineStart)) {
+ extendLeft = true;
+ }
+ if (this->str->isRightToLeft() != rightToLeft)
+ extendLeft = extendRight = false;
+
+ if (this->str->isRightToLeft()) {
+ bool tmp = extendLeft;
+ extendLeft = extendRight;
+ extendRight = tmp;
+ }
+
+ if (selStart < real_selEnd ||
+ (selWrap && fullSelectionWidth && extendRight &&
+ // don't draw the standard selection on a printer=
+ (it.key() != Q3TextDocument::Standard || !is_printer(&painter)))) {
+ int selection = it.key();
+ QColor color;
+ setColorForSelection(color, painter, pal, selection);
+ if (selStart != start || selEnd != start + len || selWrap) {
+ // have to clip
+ painter.save();
+ int cs, ce;
+ if (rightToLeft) {
+ cs = (selEnd != start + len) ?
+ this->str->at(this->str->previousCursorPosition(selEnd)).x : xstart;
+ ce = (selStart != start) ?
+ this->str->at(this->str->previousCursorPosition(selStart)).x : xstart+tmpw;
+ } else {
+ cs = (selStart != start) ? this->str->at(selStart).x : xstart;
+ ce = (selEnd != start + len) ? this->str->at(selEnd).x : xstart+tmpw;
+ }
+ QRect r(cs, y, ce-cs, h);
+ if (extendLeft)
+ r.setLeft(0);
+ if (extendRight)
+ r.setRight(fullSelectionWidth);
+ QRegion reg(r);
+ if (painter.hasClipping())
+ reg &= painter.clipRegion();
+ painter.setClipRegion(reg);
+ }
+ int xleft = xstart;
+ if (extendLeft) {
+ tmpw += xstart;
+ xleft = 0;
+ }
+ if (extendRight)
+ tmpw = fullSelectionWidth - xleft;
+ if(color.isValid())
+ painter.fillRect(xleft, y, tmpw, h, color);
+ painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0);
+ if (selStart != start || selEnd != start + len || selWrap)
+ painter.restore();
+ }
+ }
+ }
+
+ if (format->isMisspelled()) {
+ painter.save();
+ painter.setPen(QPen(Qt::red, 1, Qt::DotLine));
+ painter.drawLine(xstart, y + baseLine + 1, xstart + w, y + baseLine + 1);
+ painter.restore();
+ }
+
+ if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() &&
+ document()->focusIndicator.parag == this &&
+ ((document()->focusIndicator.start >= start &&
+ document()->focusIndicator.start + document()->focusIndicator.len <= start + len)
+ || (document()->focusIndicator.start <= start &&
+ document()->focusIndicator.start + document()->focusIndicator.len >= start + len))) {
+ QStyleOptionFocusRect opt;
+ opt.rect.setRect(xstart, y, w, h);
+#ifndef Q_WS_WIN
+ opt.state = QStyle::State_None;
+#else
+ // force drawing a focus rect but only on windows because it's
+ // configurable by the user in windows settings (see
+ // SH_UnderlineShortcut style hint) and we want to override
+ // this settings.
+ opt.state = QStyle::State_KeyboardFocusChange;
+#endif
+ opt.palette = pal;
+ QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &painter);
+ }
+}
+
+void Q3TextParagraph::drawLabel(QPainter* p, int x, int y, int w, int h, int base,
+ const QPalette& pal)
+{
+ QRect r (x, y, w, h);
+ Q3StyleSheetItem::ListStyle s = listStyle();
+
+ p->save();
+ Q3TextFormat *format = at(0)->format();
+ if (format) {
+ p->setPen(format->color());
+ p->setFont(format->font());
+ }
+ QFontMetrics fm(p->fontMetrics());
+ int size = fm.lineSpacing() / 3;
+
+ bool rtl = str->isRightToLeft();
+
+ switch (s) {
+ case Q3StyleSheetItem::ListDecimal:
+ case Q3StyleSheetItem::ListLowerAlpha:
+ case Q3StyleSheetItem::ListUpperAlpha:
+ {
+ if (list_val == -1) { // uninitialised list value, calcluate the right one
+ int depth = listDepth();
+ list_val--;
+ // ### evil, square and expensive. This needs to be done when formatting, not when painting
+ Q3TextParagraph* s = prev();
+ int depth_s;
+ while (s && (depth_s = s->listDepth()) >= depth) {
+ if (depth_s == depth && s->isListItem())
+ list_val--;
+ s = s->prev();
+ }
+ }
+
+ int n = list_val;
+ if (n < -1)
+ n = -n - 1;
+ QString l;
+ switch (s) {
+ case Q3StyleSheetItem::ListLowerAlpha:
+ if (n < 27) {
+ l = QLatin1Char(('a' + (char) (n-1)));
+ break;
+ }
+ case Q3StyleSheetItem::ListUpperAlpha:
+ if (n < 27) {
+ l = QLatin1Char(('A' + (char) (n-1)));
+ break;
+ }
+ break;
+ default: //Q3StyleSheetItem::ListDecimal:
+ l.setNum(n);
+ break;
+ }
+ if (rtl)
+ l.prepend(QLatin1String(" ."));
+ else
+ l += QString::fromLatin1(". ");
+ int x = (rtl ? r.left() : r.right() - fm.width(l));
+ p->drawText(x, r.top() + base, l);
+ }
+ break;
+ case Q3StyleSheetItem::ListSquare:
+ {
+ int x = rtl ? r.left() + size : r.right() - size*2;
+ QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->fillRect(er , pal.brush(QPalette::Text));
+ }
+ break;
+ case Q3StyleSheetItem::ListCircle:
+ {
+ int x = rtl ? r.left() + size : r.right() - size*2;
+ QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->drawEllipse(er);
+ }
+ break;
+ case Q3StyleSheetItem::ListDisc:
+ default:
+ {
+ p->setBrush(pal.brush(QPalette::Text));
+ int x = rtl ? r.left() + size : r.right() - size*2;
+ QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->drawEllipse(er);
+ p->setBrush(Qt::NoBrush);
+ }
+ break;
+ }
+
+ p->restore();
+}
+
+#ifndef QT_NO_DATASTREAM
+void Q3TextParagraph::readStyleInformation(QDataStream &stream)
+{
+ int int_align, int_lstyle;
+ uchar uchar_litem, uchar_rtext, uchar_dir;
+ stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm
+ >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir;
+ align = int_align; lstyle = (Q3StyleSheetItem::ListStyle) int_lstyle;
+ litem = uchar_litem; rtext = uchar_rtext; str->setDirection((QChar::Direction)uchar_dir);
+ Q3TextParagraph* s = prev() ? prev() : this;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+void Q3TextParagraph::writeStyleInformation(QDataStream& stream) const
+{
+ stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction();
+}
+#endif
+
+
+void Q3TextParagraph::setListItem(bool li)
+{
+ if ((bool)litem == li)
+ return;
+ litem = li;
+ changed = true;
+ Q3TextParagraph* s = prev() ? prev() : this;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+void Q3TextParagraph::setListDepth(int depth) {
+ if (!hasdoc || depth == ldepth)
+ return;
+ ldepth = depth;
+ Q3TextParagraph* s = prev() ? prev() : this;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+int *Q3TextParagraph::tabArray() const
+{
+ int *ta = tArray;
+ if (!ta && hasdoc)
+ ta = document()->tabArray();
+ return ta;
+}
+
+int Q3TextParagraph::nextTab(int, int x)
+{
+ int *ta = tArray;
+ if (hasdoc) {
+ if (!ta)
+ ta = document()->tabArray();
+ tabStopWidth = document()->tabStopWidth();
+ }
+ if (ta) {
+ int i = 0;
+ while (ta[i]) {
+ if (ta[i] >= x)
+ return tArray[i];
+ ++i;
+ }
+ return tArray[0];
+ } else {
+ int n;
+ if (tabStopWidth != 0)
+ n = x / tabStopWidth;
+ else
+ return x;
+ return tabStopWidth * (n + 1);
+ }
+}
+
+void Q3TextParagraph::adjustToPainter(QPainter *p)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int i = 0; i < length(); ++i) {
+ if (at(i)->isCustom())
+ at(i)->customItem()->adjustToPainter(p);
+ }
+#endif
+}
+
+Q3TextFormatCollection *Q3TextParagraph::formatCollection() const
+{
+ if (hasdoc)
+ return document()->formatCollection();
+ Q3TextFormatCollection* fc = &pseudoDocument()->collection;
+ if (paintdevice != fc->paintDevice())
+ fc->setPaintDevice(paintdevice);
+ return fc;
+}
+
+QString Q3TextParagraph::richText() const
+{
+ QString s;
+ Q3TextStringChar *formatChar = 0;
+ QString spaces;
+ bool doStart = richTextExportStart && richTextExportStart->paragraph() == this;
+ bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this;
+ int i;
+ QString lastAnchorName;
+ for (i = 0; i < length()-1; ++i) {
+ if (doStart && i && richTextExportStart->index() == i)
+ s += QLatin1String("<!--StartFragment-->");
+ if (doEnd && richTextExportEnd->index() == i)
+ s += QLatin1String("<!--EndFragment-->");
+ Q3TextStringChar *c = &str->at(i);
+ if (c->isAnchor() && !c->anchorName().isEmpty() && c->anchorName() != lastAnchorName) {
+ lastAnchorName = c->anchorName();
+ if (c->anchorName().contains(QLatin1Char('#'))) {
+ QStringList l = c->anchorName().split(QLatin1Char('#'));
+ for (QStringList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it)
+ s += QLatin1String("<a name=\"") + *it + QLatin1String("\"></a>");
+ } else {
+ s += QLatin1String("<a name=\"") + c->anchorName() + QLatin1String("\"></a>");
+ }
+ }
+ if (!formatChar) {
+ s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(),
+ 0, QString(), c->anchorHref());
+ formatChar = c;
+ } else if ((formatChar->format()->key() != c->format()->key()) ||
+ (c->anchorHref() != formatChar->anchorHref())) {
+ s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(),
+ formatChar->format() , formatChar->anchorHref(), c->anchorHref());
+ formatChar = c;
+ }
+ if (c->c == QLatin1Char('<'))
+ s += QLatin1String("&lt;");
+ else if (c->c == QLatin1Char('>'))
+ s += QLatin1String("&gt;");
+ else if (c->c == QLatin1Char('&'))
+ s += QLatin1String("&amp;");
+ else if (c->c == QLatin1Char('\"'))
+ s += QLatin1String("&quot;");
+#ifndef QT_NO_TEXTCUSTOMITEM
+ else if (c->isCustom())
+ s += c->customItem()->richText();
+#endif
+ else if (c->c == QLatin1Char('\n') || c->c == QChar::LineSeparator)
+ s += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
+ else
+ s += c->c;
+ }
+ if (doEnd && richTextExportEnd->index() == i)
+ s += QLatin1String("<!--EndFragment-->");
+ if (formatChar)
+ s += formatChar->format()->makeFormatEndTags(formatCollection()->defaultFormat(), formatChar->anchorHref());
+ return s;
+}
+
+void Q3TextParagraph::addCommand(Q3TextCommand *cmd)
+{
+ if (!hasdoc)
+ pseudoDocument()->commandHistory->addCommand(cmd);
+ else
+ document()->commands()->addCommand(cmd);
+}
+
+Q3TextCursor *Q3TextParagraph::undo(Q3TextCursor *c)
+{
+ if (!hasdoc)
+ return pseudoDocument()->commandHistory->undo(c);
+ return document()->commands()->undo(c);
+}
+
+Q3TextCursor *Q3TextParagraph::redo(Q3TextCursor *c)
+{
+ if (!hasdoc)
+ return pseudoDocument()->commandHistory->redo(c);
+ return document()->commands()->redo(c);
+}
+
+int Q3TextParagraph::topMargin() const
+{
+ int m = 0;
+ if (rtext) {
+ m = isListItem() ? (document()->li_tm/qMax(1,listDepth()*listDepth())) :
+ (listDepth() ? 0 : document()->par_tm);
+ if (listDepth() == 1 &&( !prev() || prev()->listDepth() < listDepth()))
+ m = qMax<int>(m, document()->list_tm);
+ }
+ m += utm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::bottomMargin() const
+{
+ int m = 0;
+ if (rtext) {
+ m = isListItem() ? (document()->li_bm/qMax(1,listDepth()*listDepth())) :
+ (listDepth() ? 0 : document()->par_bm);
+ if (listDepth() == 1 &&( !next() || next()->listDepth() < listDepth()))
+ m = qMax<int>(m, document()->list_bm);
+ }
+ m += ubm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::leftMargin() const
+{
+ int m = ulm;
+ if (listDepth() && !string()->isRightToLeft())
+ m += listDepth() * document()->list_lm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::firstLineMargin() const
+{
+ int m = uflm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::rightMargin() const
+{
+ int m = urm;
+ if (listDepth() && string()->isRightToLeft())
+ m += listDepth() * document()->list_lm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::lineSpacing() const
+{
+ int l = ulinespacing;
+ l = scale(l, Q3TextFormat::painter());
+ return l;
+}
+
+void Q3TextParagraph::copyParagData(Q3TextParagraph *parag)
+{
+ rtext = parag->rtext;
+ lstyle = parag->lstyle;
+ ldepth = parag->ldepth;
+ litem = parag->litem;
+ align = parag->align;
+ utm = parag->utm;
+ ubm = parag->ubm;
+ urm = parag->urm;
+ ulm = parag->ulm;
+ uflm = parag->uflm;
+ ulinespacing = parag->ulinespacing;
+ QColor *c = parag->backgroundColor();
+ if (c)
+ setBackgroundColor(*c);
+ str->setDirection(parag->str->direction());
+}
+
+void Q3TextParagraph::show()
+{
+ if (visible || !hasdoc)
+ return;
+ visible = true;
+}
+
+void Q3TextParagraph::hide()
+{
+ if (!visible || !hasdoc)
+ return;
+ visible = false;
+}
+
+void Q3TextParagraph::setDirection(QChar::Direction dir)
+{
+ if (str && str->direction() != dir) {
+ str->setDirection(dir);
+ invalidate(0);
+ }
+}
+
+QChar::Direction Q3TextParagraph::direction() const
+{
+ return (str ? str->direction() : QChar::DirON);
+}
+
+void Q3TextParagraph::setChanged(bool b, bool recursive)
+{
+ changed = b;
+ if (recursive) {
+ if (document() && document()->parentParagraph())
+ document()->parentParagraph()->setChanged(b, recursive);
+ }
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+Q3TextPreProcessor::Q3TextPreProcessor()
+{
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatter::Q3TextFormatter()
+ : thisminw(0), thiswused(0), wrapEnabled(true), wrapColumn(-1), biw(false)
+{
+}
+
+QTextLineStart *Q3TextFormatter::formatLine(Q3TextParagraph *parag, Q3TextString *string, QTextLineStart *line,
+ Q3TextStringChar *startChar, Q3TextStringChar *lastChar, int align, int space)
+{
+ if (lastChar < startChar)
+ return new QTextLineStart;
+#ifndef QT_NO_COMPLEXTEXT
+ if(string->isBidi())
+ return bidiReorderLine(parag, string, line, startChar, lastChar, align, space);
+#endif
+ int start = (startChar - &string->at(0));
+ int last = (lastChar - &string->at(0));
+
+ // ignore white space at the end of the line.
+ Q3TextStringChar *ch = lastChar;
+ while (ch > startChar && ch->whiteSpace) {
+ space += ch->format()->width(QLatin1Char(' '));
+ --ch;
+ }
+
+ if (space < 0)
+ space = 0;
+
+ // do alignment Auto == Left in this case
+ if (align & Qt::AlignHCenter || align & Qt::AlignRight) {
+ if (align & Qt::AlignHCenter)
+ space /= 2;
+ for (int j = start; j <= last; ++j)
+ string->at(j).x += space;
+ } else if (align & Qt::AlignJustify) {
+ int numSpaces = 0;
+ // End at "last-1", the last space ends up with a width of 0
+ for (int j = last-1; j >= start; --j) {
+ // Start at last tab, if any.
+ Q3TextStringChar &ch = string->at(j);
+ if (ch.c == QLatin1Char('\t')) {
+ start = j+1;
+ break;
+ }
+ if(ch.whiteSpace)
+ numSpaces++;
+ }
+ int toAdd = 0;
+ for (int k = start + 1; k <= last; ++k) {
+ Q3TextStringChar &ch = string->at(k);
+ if(numSpaces && ch.whiteSpace) {
+ int s = space / numSpaces;
+ toAdd += s;
+ space -= s;
+ numSpaces--;
+ }
+ string->at(k).x += toAdd;
+ }
+ }
+
+ if (last >= 0 && last < string->length())
+ line->w = string->at(last).x + string->width(last);
+ else
+ line->w = 0;
+
+ return new QTextLineStart;
+}
+
+#ifndef QT_NO_COMPLEXTEXT
+
+#ifdef BIDI_DEBUG
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <iostream>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+// collects one line of the paragraph and transforms it to visual order
+QTextLineStart *Q3TextFormatter::bidiReorderLine(Q3TextParagraph * /*parag*/, Q3TextString *text, QTextLineStart *line,
+ Q3TextStringChar *startChar, Q3TextStringChar *lastChar, int align, int space)
+{
+ // ignore white space at the end of the line.
+ int endSpaces = 0;
+ while (lastChar > startChar && lastChar->whiteSpace) {
+ space += lastChar->format()->width(QLatin1Char(' '));
+ --lastChar;
+ ++endSpaces;
+ }
+
+ int start = (startChar - &text->at(0));
+ int last = (lastChar - &text->at(0));
+
+ int length = lastChar - startChar + 1;
+
+
+ int x = startChar->x;
+
+ unsigned char _levels[256];
+ int _visual[256];
+
+ unsigned char *levels = _levels;
+ int *visual = _visual;
+
+ if (length > 255) {
+ levels = (unsigned char *)malloc(length*sizeof(unsigned char));
+ visual = (int *)malloc(length*sizeof(int));
+ }
+
+ //qDebug("bidiReorderLine: length=%d (%d-%d)", length, start, last);
+
+ Q3TextStringChar *ch = startChar;
+ unsigned char *l = levels;
+ while (ch <= lastChar) {
+ //qDebug(" level: %d", ch->bidiLevel);
+ *(l++) = (ch++)->bidiLevel;
+ }
+
+ QTextEngine::bidiReorder(length, levels, visual);
+
+ // now construct the reordered string out of the runs...
+
+ int numSpaces = 0;
+ align = QStyle::visualAlignment(text->isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight, QFlag(align));
+
+ // This is not really correct, but as we can't make the scroll bar move to the left of the origin,
+ // this ensures all text can be scrolled to and read.
+ if (space < 0)
+ space = 0;
+
+ if (align & Qt::AlignHCenter)
+ x += space/2;
+ else if (align & Qt::AlignRight)
+ x += space;
+ else if (align & Qt::AlignJustify) {
+ // End at "last-1", the last space ends up with a width of 0
+ for (int j = last-1; j >= start; --j) {
+ // Start at last tab, if any.
+ Q3TextStringChar &ch = text->at(j);
+ if (ch.c == QLatin1Char('\t')) {
+ start = j+1;
+ break;
+ }
+ if(ch.whiteSpace)
+ numSpaces++;
+ }
+ }
+
+ int toAdd = 0;
+ int xorig = x;
+ Q3TextStringChar *lc = startChar + visual[0];
+ for (int i = 0; i < length; i++) {
+ Q3TextStringChar *ch = startChar + visual[i];
+ if (numSpaces && ch->whiteSpace) {
+ int s = space / numSpaces;
+ toAdd += s;
+ space -= s;
+ numSpaces--;
+ }
+
+ if (lc->format() != ch->format() && !ch->c.isSpace()
+ && lc->format()->font().italic() && !ch->format()->font().italic()) {
+ int rb = lc->format()->fontMetrics().rightBearing(lc->c);
+ if (rb < 0)
+ x -= rb;
+ }
+
+ ch->x = x + toAdd;
+ ch->rightToLeft = ch->bidiLevel % 2;
+ //qDebug("visual: %d (%p) placed at %d rightToLeft=%d", visual[i], ch, x +toAdd, ch->rightToLeft );
+ int ww = 0;
+ if (ch->c.unicode() >= 32 || ch->c == QLatin1Char(QLatin1Char('\t')) || ch->c == QLatin1Char('\n') || ch->isCustom()) {
+ ww = text->width(start+visual[i]);
+ } else {
+ ww = ch->format()->width(QLatin1Char(' '));
+ }
+ x += ww;
+ lc = ch;
+ }
+ x += toAdd;
+
+ while (endSpaces--) {
+ ++lastChar;
+ int sw = lastChar->format()->width(QLatin1Char(' '));
+ if (text->isRightToLeft()) {
+ xorig -= sw;
+ lastChar->x = xorig;
+ ch->rightToLeft = true;
+ } else {
+ lastChar->x = x;
+ x += sw;
+ ch->rightToLeft = false;
+ }
+ }
+
+ line->w = x;
+
+ if (length > 255) {
+ free(levels);
+ free(visual);
+ }
+
+ return new QTextLineStart;
+}
+#endif
+
+
+void Q3TextFormatter::insertLineStart(Q3TextParagraph *parag, int index, QTextLineStart *ls)
+{
+ QMap<int, QTextLineStart*>::Iterator it;
+ if ((it = parag->lineStartList().find(index)) == parag->lineStartList().end()) {
+ parag->lineStartList().insert(index, ls);
+ } else {
+ delete *it;
+ parag->lineStartList().erase(it);
+ parag->lineStartList().insert(index, ls);
+ }
+}
+
+
+/* Standard pagebreak algorithm using Q3TextFlow::adjustFlow. Returns
+ the shift of the paragraphs bottom line.
+ */
+int Q3TextFormatter::formatVertically(Q3TextDocument* doc, Q3TextParagraph* parag)
+{
+ int oldHeight = parag->rect().height();
+ QMap<int, QTextLineStart*>& lineStarts = parag->lineStartList();
+ QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
+ int h = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
+ for (; it != lineStarts.end() ; ++it ) {
+ QTextLineStart * ls = it.value();
+ ls->y = h;
+ Q3TextStringChar *c = &parag->string()->at(it.key());
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (c && c->customItem() && c->customItem()->ownLine()) {
+ int h = c->customItem()->height;
+ c->customItem()->pageBreak(parag->rect().y() + ls->y + ls->baseLine - h, doc->flow());
+ int delta = c->customItem()->height - h;
+ ls->h += delta;
+ if (delta)
+ parag->setMovedDown(true);
+ } else
+#endif
+ {
+
+ int shift = doc->flow()->adjustFlow(parag->rect().y() + ls->y, ls->w, ls->h);
+ ls->y += shift;
+ if (shift)
+ parag->setMovedDown(true);
+ }
+ h = ls->y + ls->h;
+ }
+ int m = parag->bottomMargin();
+ if (!parag->next())
+ m = 0;
+ else
+ m = qMax(m, parag->next()->topMargin()) / 2;
+ h += m;
+ parag->setHeight(h);
+ return h - oldHeight;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatterBreakInWords::Q3TextFormatterBreakInWords()
+{
+}
+
+#define SPACE(s) s
+
+int Q3TextFormatterBreakInWords::format(Q3TextDocument *doc,Q3TextParagraph *parag,
+ int start, const QMap<int, QTextLineStart*> &)
+{
+ // make sure bidi information is correct.
+ (void)parag->string()->isBidi();
+
+ Q3TextStringChar *c = 0;
+ Q3TextStringChar *firstChar = 0;
+ int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
+ int x = left + (doc ? parag->firstLineMargin() : 0);
+ int dw = parag->documentVisibleWidth() - (doc ? doc->rightMargin() : 0);
+ int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
+ int h = y;
+ int len = parag->length();
+ if (doc)
+ x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 4);
+ int rm = parag->rightMargin();
+ int w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ bool fullWidth = true;
+ int minw = 0;
+ int wused = 0;
+ bool wrapEnabled = isWrapEnabled(parag);
+
+ start = 0; //######### what is the point with start?! (Matthias)
+ if (start == 0)
+ c = &parag->string()->at(0);
+
+ int i = start;
+ QTextLineStart *lineStart = new QTextLineStart(y, y, 0);
+ insertLineStart(parag, 0, lineStart);
+
+ QPainter *painter = Q3TextFormat::painter();
+
+ int col = 0;
+ int ww = 0;
+ QChar lastChr;
+ int tabBase = left < x ? left : x;
+ for (; i < len; ++i, ++col) {
+ if (c)
+ lastChr = c->c;
+ c = &parag->string()->at(i);
+ // ### the lines below should not be needed
+ if (painter)
+ c->format()->setPainter(painter);
+ if (i > 0) {
+ c->lineStart = 0;
+ } else {
+ c->lineStart = 1;
+ firstChar = c;
+ }
+ if (c->c.unicode() >= 32 || c->isCustom()) {
+ ww = parag->string()->width(i);
+ } else if (c->c == QLatin1Char('\t')) {
+ int nx = parag->nextTab(i, x - tabBase) + tabBase;
+ if (nx < x)
+ ww = w - x;
+ else
+ ww = nx - x;
+ } else {
+ ww = c->format()->width(QLatin1Char(' '));
+ }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (c->isCustom() && c->customItem()->ownLine()) {
+ x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ c->customItem()->resize(w - x);
+ w = dw;
+ y += h;
+ h = c->height();
+ lineStart = new QTextLineStart(y, h, h);
+ insertLineStart(parag, i, lineStart);
+ c->lineStart = 1;
+ firstChar = c;
+ x = 0xffffff;
+ continue;
+ }
+#endif
+
+ if (wrapEnabled &&
+ ((wrapAtColumn() == -1 && x + ww > w) ||
+ (wrapAtColumn() != -1 && col >= wrapAtColumn()))) {
+ x = doc ? parag->document()->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw;
+ y += h;
+ h = c->height();
+ lineStart = formatLine(parag, parag->string(), lineStart, firstChar, c-1);
+ lineStart->y = y;
+ insertLineStart(parag, i, lineStart);
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ col = 0;
+ if (wrapAtColumn() != -1)
+ minw = qMax(minw, w);
+ } else if (lineStart) {
+ lineStart->baseLine = qMax(lineStart->baseLine, c->ascent());
+ h = qMax(h, c->height());
+ lineStart->h = h;
+ }
+
+ c->x = x;
+ x += ww;
+ wused = qMax(wused, x);
+ }
+
+ int m = parag->bottomMargin();
+ if (!parag->next())
+ m = 0;
+ else
+ m = qMax(m, parag->next()->topMargin()) / 2;
+ parag->setFullWidth(fullWidth);
+ y += h + m;
+ if (doc)
+ minw += doc->rightMargin();
+ if (!wrapEnabled)
+ minw = qMax(minw, wused);
+
+ thisminw = minw;
+ thiswused = wused;
+ return y;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatterBreakWords::Q3TextFormatterBreakWords()
+{
+}
+
+#define DO_FLOW(lineStart) do{ if (doc && doc->isPageBreakEnabled()) { \
+ int yflow = lineStart->y + parag->rect().y();\
+ int shift = doc->flow()->adjustFlow(yflow, dw, lineStart->h); \
+ lineStart->y += shift;\
+ y += shift;\
+ }}while(false)
+
+int Q3TextFormatterBreakWords::format(Q3TextDocument *doc, Q3TextParagraph *parag,
+ int start, const QMap<int, QTextLineStart*> &)
+{
+ // make sure bidi information is correct.
+ (void)parag->string()->isBidi();
+
+ Q3TextStringChar *c = 0;
+ Q3TextStringChar *firstChar = 0;
+ Q3TextString *string = parag->string();
+ int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
+ int x = left + (doc ? parag->firstLineMargin() : 0);
+ int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
+ int h = y;
+ int len = parag->length();
+ if (doc)
+ x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 0);
+ int dw = parag->documentVisibleWidth() - (doc ? (left != x ? 0 : doc->rightMargin()) : 0);
+
+ int curLeft = x;
+ int rm = parag->rightMargin();
+ int rdiff = doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 0) : 0;
+ int w = dw - rdiff;
+ bool fullWidth = true;
+ int marg = left + rdiff;
+ int minw = 0;
+ int wused = 0;
+ int tminw = marg;
+ int linespacing = doc ? parag->lineSpacing() : 0;
+ bool wrapEnabled = isWrapEnabled(parag);
+
+ start = 0;
+
+ int i = start;
+ QTextLineStart *lineStart = new QTextLineStart(y, y, 0);
+ insertLineStart(parag, 0, lineStart);
+ int lastBreak = -1;
+ int tmpBaseLine = 0, tmph = 0;
+ bool lastWasNonInlineCustom = false;
+
+ int align = parag->alignment();
+ if (align == Qt::AlignAuto && doc && doc->alignment() != Qt::AlignAuto)
+ align = doc->alignment();
+
+ align &= Qt::AlignHorizontal_Mask;
+
+ // ### hack. The last char in the paragraph is always invisible,
+ // ### and somehow sometimes has a wrong format. It changes
+ // ### between // layouting and printing. This corrects some
+ // ### layouting errors in BiDi mode due to this.
+ if (len > 1) {
+ c = &parag->string()->at(len - 1);
+ if (!c->isAnchor()) {
+ if (c->format())
+ c->format()->removeRef();
+ c->setFormat(string->at(len - 2).format());
+ if (c->format())
+ c->format()->addRef();
+ }
+ }
+
+ c = &parag->string()->at(0);
+
+ QPainter *painter = Q3TextFormat::painter();
+ int col = 0;
+ int ww = 0;
+ QChar lastChr = c->c;
+ Q3TextFormat *lastFormat = c->format();
+ int tabBase = left < x ? left : x;
+ for (; i < len; ++i, ++col) {
+ if (i) {
+ c = &parag->string()->at(i-1);
+ lastChr = c->c;
+ lastFormat = c->format();
+ }
+ bool lastWasOwnLineCustomItem = lastBreak == -2;
+ bool hadBreakableChar = lastBreak != -1;
+ bool lastWasHardBreak = lastChr == QChar::LineSeparator;
+
+ // ### next line should not be needed
+ if (painter)
+ c->format()->setPainter(painter);
+ c = &string->at(i);
+
+ if (lastFormat != c->format() && !c->c.isSpace()
+ && lastFormat->font().italic() && !c->format()->font().italic()) {
+ int rb = lastFormat->fontMetrics().rightBearing(lastChr);
+ if (rb < 0)
+ x -= rb;
+ }
+
+ if ((i > 0 && (x > curLeft || ww == 0)) || lastWasNonInlineCustom) {
+ c->lineStart = 0;
+ } else {
+ c->lineStart = 1;
+ firstChar = c;
+ }
+
+ // ignore non spacing marks for column count.
+ if (col != 0 && QChar::category(c->c.unicode()) == QChar::Mark_NonSpacing)
+ --col;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ lastWasNonInlineCustom = (c->isCustom() && c->customItem()->placement() != Q3TextCustomItem::PlaceInline);
+#endif
+
+ if (c->c.unicode() >= 32 || c->isCustom()) {
+ ww = string->width(i);
+ } else if (c->c == QLatin1Char('\t')) {
+ if (align == Qt::AlignRight || align == Qt::AlignCenter) {
+ // we can not (yet) do tabs
+ ww = c->format()->width(QLatin1Char(' '));
+ } else {
+ int tabx = lastWasHardBreak ? (left + (doc ? parag->firstLineMargin() : 0)) : x;
+ int nx = parag->nextTab(i, tabx - tabBase) + tabBase;
+ if (nx < tabx) // strrrange...
+ ww = 0;
+ else
+ ww = nx - tabx;
+ }
+ } else {
+ ww = c->format()->width(QLatin1Char(' '));
+ }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem* ci = c->customItem();
+ if (c->isCustom() && ci->ownLine()) {
+ QTextLineStart *lineStart2 = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x - ww));
+ x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ ci->resize(w - x);
+ if (ci->width < w - x) {
+ if (align & Qt::AlignHCenter)
+ x = (w - ci->width) / 2;
+ else if (align & Qt::AlignRight) {
+ x = w - ci->width;
+ }
+ }
+ c->x = x;
+ curLeft = x;
+ if (i == 0 || !isBreakable(string, i-1) ||
+ string->at(i - 1).lineStart == 0) {
+ y += qMax(h, qMax(tmph, linespacing));
+ tmph = c->height();
+ h = tmph;
+ lineStart = lineStart2;
+ lineStart->y = y;
+ insertLineStart(parag, i, lineStart);
+ c->lineStart = 1;
+ firstChar = c;
+ } else {
+ tmph = c->height();
+ h = tmph;
+ delete lineStart2;
+ }
+ lineStart->h = h;
+ lineStart->baseLine = h;
+ tmpBaseLine = lineStart->baseLine;
+ lastBreak = -2;
+ x = w;
+ minw = qMax(minw, tminw);
+
+ int tw = ci->minimumWidth() + (doc ? doc->leftMargin() : 0);
+ if (tw < QWIDGETSIZE_MAX)
+ tminw = tw;
+ else
+ tminw = marg;
+ wused = qMax(wused, ci->width);
+ continue;
+ } else if (c->isCustom() && ci->placement() != Q3TextCustomItem::PlaceInline) {
+ int tw = ci->minimumWidth();
+ if (tw < QWIDGETSIZE_MAX)
+ minw = qMax(minw, tw);
+ }
+#endif
+ // we break if
+ // 1. the last character was a hard break (QChar::LineSeparator) or
+ // 2. the last character was a own-line custom item (eg. table or ruler) or
+ // 3. wrapping was enabled, it was not a space and following
+ // condition is true: We either had a breakable character
+ // previously or we ar allowed to break in words and - either
+ // we break at w pixels and the current char would exceed that
+ // or - we break at a column and the current character would
+ // exceed that.
+ if (lastWasHardBreak || lastWasOwnLineCustomItem ||
+ (wrapEnabled &&
+ ((!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) &&
+ ((wrapAtColumn() == -1 && x + ww > w) ||
+ (wrapAtColumn() != -1 && col >= wrapAtColumn()))))
+ )
+ ) {
+ if (wrapAtColumn() != -1)
+ minw = qMax(minw, x + ww);
+ // if a break was forced (no breakable char, hard break or own line custom item), break immediately....
+ if (!hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem) {
+ if (lineStart) {
+ lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
+ h = qMax(h, tmph);
+ lineStart->h = h;
+ DO_FLOW(lineStart);
+ }
+ lineStart = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x));
+ x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ if (!doc && c->c == QLatin1Char('\t')) { // qt_format_text tab handling
+ int nx = parag->nextTab(i, x - tabBase) + tabBase;
+ if (nx < x)
+ ww = w - x;
+ else
+ ww = nx - x;
+ }
+ curLeft = x;
+ y += qMax(h, linespacing);
+ tmph = c->height();
+ h = 0;
+ lineStart->y = y;
+ insertLineStart(parag, i, lineStart);
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ tmpBaseLine = lineStart->baseLine;
+ lastBreak = -1;
+ col = 0;
+ if (allowBreakInWords() || lastWasHardBreak) {
+ minw = qMax(minw, tminw);
+ tminw = marg + ww;
+ }
+ } else { // ... otherwise if we had a breakable char, break there
+ DO_FLOW(lineStart);
+ c->x = x;
+ i = lastBreak;
+ lineStart = formatLine(parag, string, lineStart, firstChar, parag->at(lastBreak),align, SPACE(w - string->at(i+1).x));
+ x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ if (!doc && c->c == QLatin1Char('\t')) { // qt_format_text tab handling
+ int nx = parag->nextTab(i, x - tabBase) + tabBase;
+ if (nx < x)
+ ww = w - x;
+ else
+ ww = nx - x;
+ }
+ curLeft = x;
+ y += qMax(h, linespacing);
+ tmph = c->height();
+ h = tmph;
+ lineStart->y = y;
+ insertLineStart(parag, i + 1, lineStart);
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ tmpBaseLine = lineStart->baseLine;
+ lastBreak = -1;
+ col = 0;
+ minw = qMax(minw, tminw);
+ tminw = marg;
+ continue;
+ }
+ } else if (lineStart && isBreakable(string, i)) {
+ if (len <= 2 || i < len - 1) {
+ tmpBaseLine = qMax(tmpBaseLine, c->ascent());
+ tmph = qMax(tmph, c->height());
+ }
+ minw = qMax(minw, tminw);
+
+ tminw = marg + ww;
+ lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
+ h = qMax(h, tmph);
+ lineStart->h = h;
+ if (i < len - 2 || c->c != QLatin1Char(' '))
+ lastBreak = i;
+ } else {
+ tminw += ww;
+ int cascent = c->ascent();
+ int cheight = c->height();
+ int belowBaseLine = qMax(tmph - tmpBaseLine, cheight-cascent);
+ tmpBaseLine = qMax(tmpBaseLine, cascent);
+ tmph = tmpBaseLine + belowBaseLine;
+ }
+
+ c->x = x;
+ x += ww;
+ wused = qMax(wused, x);
+ }
+
+ if (lineStart) {
+ lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
+ h = qMax(h, tmph);
+ lineStart->h = h;
+ // last line in a paragraph is not justified
+ if (align & Qt::AlignJustify) {
+ align |= Qt::AlignLeft;
+ align &= ~(Qt::AlignJustify|Qt::AlignAbsolute);
+ }
+ DO_FLOW(lineStart);
+ lineStart = formatLine(parag, string, lineStart, firstChar, c, align, SPACE(w - x));
+ delete lineStart;
+ }
+
+ minw = qMax(minw, tminw);
+ if (doc)
+ minw += doc->rightMargin();
+
+ int m = parag->bottomMargin();
+ if (!parag->next())
+ m = 0;
+ else
+ m = qMax(m, parag->next()->topMargin()) / 2;
+ parag->setFullWidth(fullWidth);
+ y += qMax(h, linespacing) + m;
+
+ wused += rm;
+ if (!wrapEnabled || wrapAtColumn() != -1)
+ minw = qMax(minw, wused);
+
+ // This is the case where we are breaking wherever we darn well please
+ // in cases like that, the minw should not be the length of the entire
+ // word, because we necessarily want to show the word on the whole line.
+ // example: word wrap in iconview
+ if (allowBreakInWords() && minw > wused)
+ minw = wused;
+
+ thisminw = minw;
+ thiswused = wused;
+ return y;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextIndent::Q3TextIndent()
+{
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatCollection::Q3TextFormatCollection()
+ : paintdevice(0)
+{
+ defFormat = new Q3TextFormat(QApplication::font(),
+ QApplication::palette().color(QPalette::Active, QPalette::Text));
+ lastFormat = cres = 0;
+ cflags = -1;
+ cachedFormat = 0;
+}
+
+Q3TextFormatCollection::~Q3TextFormatCollection()
+{
+ QHash<QString, Q3TextFormat *>::ConstIterator it = cKey.constBegin();
+ while (it != cKey.constEnd()) {
+ delete it.value();
+ ++it;
+ }
+ delete defFormat;
+}
+
+void Q3TextFormatCollection::setPaintDevice(QPaintDevice *pd)
+{
+ paintdevice = pd;
+
+#if defined(Q_WS_X11)
+ int scr = (paintdevice) ? paintdevice->x11Screen() : QX11Info::appScreen();
+
+ defFormat->fn.x11SetScreen(scr);
+ defFormat->update();
+
+ QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin();
+ for (; it != cKey.end(); ++it) {
+ Q3TextFormat *format = *it;
+ format->fn.x11SetScreen(scr);
+ format->update();
+ }
+#endif // Q_WS_X11
+}
+
+Q3TextFormat *Q3TextFormatCollection::format(Q3TextFormat *f)
+{
+ if (f->parent() == this || f == defFormat) {
+ lastFormat = f;
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ if (f == lastFormat || (lastFormat && f->key() == lastFormat->key())) {
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ Q3TextFormat *fm = cKey.value(f->key());
+ if (fm) {
+ lastFormat = fm;
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ if (f->key() == defFormat->key())
+ return defFormat;
+
+ lastFormat = createFormat(*f);
+ lastFormat->collection = this;
+ cKey.insert(lastFormat->key(), lastFormat);
+ return lastFormat;
+}
+
+Q3TextFormat *Q3TextFormatCollection::format(Q3TextFormat *of, Q3TextFormat *nf, int flags)
+{
+ if (cres && kof == of->key() && knf == nf->key() && cflags == flags) {
+ cres->addRef();
+ return cres;
+ }
+
+ cres = createFormat(*of);
+ kof = of->key();
+ knf = nf->key();
+ cflags = flags;
+ if (flags & Q3TextFormat::Bold)
+ cres->fn.setBold(nf->fn.bold());
+ if (flags & Q3TextFormat::Italic)
+ cres->fn.setItalic(nf->fn.italic());
+ if (flags & Q3TextFormat::Underline)
+ cres->fn.setUnderline(nf->fn.underline());
+ if (flags & Q3TextFormat::StrikeOut)
+ cres->fn.setStrikeOut(nf->fn.strikeOut());
+ if (flags & Q3TextFormat::Family)
+ cres->fn.setFamily(nf->fn.family());
+ if (flags & Q3TextFormat::Size) {
+ if (of->usePixelSizes)
+ cres->fn.setPixelSize(nf->fn.pixelSize());
+ else
+ cres->fn.setPointSize(nf->fn.pointSize());
+ }
+ if (flags & Q3TextFormat::Color)
+ cres->col = nf->col;
+ if (flags & Q3TextFormat::Misspelled)
+ cres->missp = nf->missp;
+ if (flags & Q3TextFormat::VAlign)
+ cres->ha = nf->ha;
+ cres->update();
+
+ Q3TextFormat *fm = cKey.value(cres->key());
+ if (!fm) {
+ cres->collection = this;
+ cKey.insert(cres->key(), cres);
+ } else {
+ delete cres;
+ cres = fm;
+ cres->addRef();
+ }
+
+ return cres;
+}
+
+Q3TextFormat *Q3TextFormatCollection::format(const QFont &f, const QColor &c)
+{
+ if (cachedFormat && cfont == f && ccol == c) {
+ cachedFormat->addRef();
+ return cachedFormat;
+ }
+
+ QString key = Q3TextFormat::getKey(f, c, false, Q3TextFormat::AlignNormal);
+ cachedFormat = cKey.value(key);
+ cfont = f;
+ ccol = c;
+
+ if (cachedFormat) {
+ cachedFormat->addRef();
+ return cachedFormat;
+ }
+
+ if (key == defFormat->key())
+ return defFormat;
+
+ cachedFormat = createFormat(f, c);
+ cachedFormat->collection = this;
+ cKey.insert(cachedFormat->key(), cachedFormat);
+ if (cachedFormat->key() != key)
+ qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1());
+ return cachedFormat;
+}
+
+void Q3TextFormatCollection::remove(Q3TextFormat *f)
+{
+ if (lastFormat == f)
+ lastFormat = 0;
+ if (cres == f)
+ cres = 0;
+ if (cachedFormat == f)
+ cachedFormat = 0;
+ if (cKey.value(f->key()) == f)
+ delete cKey.take(f->key());
+}
+
+#define UPDATE(up, lo, rest) \
+ if (font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest()) \
+ fm->fn.set##up##rest(font.lo##rest())
+
+void Q3TextFormatCollection::updateDefaultFormat(const QFont &font, const QColor &color, Q3StyleSheet *sheet)
+{
+ bool usePixels = font.pointSize() == -1;
+ bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() :
+ font.pointSize() != defFormat->fn.pointSize();
+ int base = usePixels ? font.pixelSize() : font.pointSize();
+ QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin();
+ for (; it != cKey.end(); ++it) {
+ Q3TextFormat *fm = *it;
+ UPDATE(F, f, amily);
+ UPDATE(W, w, eight);
+ UPDATE(B, b, old);
+ UPDATE(I, i, talic);
+ UPDATE(U, u, nderline);
+ if (changeSize) {
+ fm->stdSize = base;
+ fm->usePixelSizes = usePixels;
+ if (usePixels)
+ fm->fn.setPixelSize(fm->stdSize);
+ else
+ fm->fn.setPointSize(fm->stdSize);
+ sheet->scaleFont(fm->fn, fm->logicalFontSize);
+ }
+ if (color.isValid() && color != defFormat->col && fm->col == defFormat->col)
+ fm->col = color;
+ fm->update();
+ }
+
+ defFormat->fn = font;
+ defFormat->col = color;
+ defFormat->update();
+ defFormat->stdSize = base;
+ defFormat->usePixelSizes = usePixels;
+
+ updateKeys();
+}
+
+// the keys in cKey have changed, rebuild the hashtable
+void Q3TextFormatCollection::updateKeys()
+{
+ if (cKey.isEmpty())
+ return;
+ Q3TextFormat** formats = new Q3TextFormat *[cKey.count() + 1];
+ Q3TextFormat **f = formats;
+ for (QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin(); it != cKey.end(); ++it, ++f)
+ *f = *it;
+ *f = 0;
+ cKey.clear();
+ for (f = formats; *f; f++)
+ cKey.insert((*f)->key(), *f);
+ delete [] formats;
+}
+
+
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void Q3TextFormat::setBold(bool b)
+{
+ if (b == fn.bold())
+ return;
+ fn.setBold(b);
+ update();
+}
+
+void Q3TextFormat::setMisspelled(bool b)
+{
+ if (b == (bool)missp)
+ return;
+ missp = b;
+ update();
+}
+
+void Q3TextFormat::setVAlign(VerticalAlignment a)
+{
+ if (a == ha)
+ return;
+ ha = a;
+ update();
+}
+
+void Q3TextFormat::setItalic(bool b)
+{
+ if (b == fn.italic())
+ return;
+ fn.setItalic(b);
+ update();
+}
+
+void Q3TextFormat::setUnderline(bool b)
+{
+ if (b == fn.underline())
+ return;
+ fn.setUnderline(b);
+ update();
+}
+
+void Q3TextFormat::setStrikeOut(bool b)
+{
+ if (b == fn.strikeOut())
+ return;
+ fn.setStrikeOut(b);
+ update();
+}
+
+void Q3TextFormat::setFamily(const QString &f)
+{
+ if (f == fn.family())
+ return;
+ fn.setFamily(f);
+ update();
+}
+
+void Q3TextFormat::setPointSize(int s)
+{
+ if (s == fn.pointSize())
+ return;
+ fn.setPointSize(s);
+ usePixelSizes = false;
+ update();
+}
+
+void Q3TextFormat::setFont(const QFont &f)
+{
+ if (f == fn && !k.isEmpty())
+ return;
+ fn = f;
+ update();
+}
+
+void Q3TextFormat::setColor(const QColor &c)
+{
+ if (c == col)
+ return;
+ col = c;
+ update();
+}
+
+QString Q3TextFormat::makeFormatChangeTags(Q3TextFormat* defaultFormat, Q3TextFormat *f,
+ const QString& oldAnchorHref, const QString& anchorHref ) const
+{
+ QString tag;
+ if (f)
+ tag += f->makeFormatEndTags(defaultFormat, oldAnchorHref);
+
+ if (!anchorHref.isEmpty())
+ tag += QLatin1String("<a href=\"") + anchorHref + QLatin1String("\">");
+
+ if (font() != defaultFormat->font()
+ || vAlign() != defaultFormat->vAlign()
+ || color().rgb() != defaultFormat->color().rgb()) {
+ QString s;
+ if (font().family() != defaultFormat->font().family())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-family:") + fn.family();
+ if (font().italic() && font().italic() != defaultFormat->font().italic())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-style:") + (font().italic() ? QLatin1String("italic") : QLatin1String("normal"));
+ if (font().pointSize() != defaultFormat->font().pointSize())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-size:") + QString::number(fn.pointSize()) + QLatin1String("pt");
+ if (font().weight() != defaultFormat->font().weight())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-weight:") + QString::number(fn.weight() * 8);
+ QString textDecoration;
+ bool none = false;
+ if ( font().underline() != defaultFormat->font().underline() ) {
+ if (font().underline())
+ textDecoration = QLatin1String("underline");
+ else
+ none = true;
+ }
+ if ( font().overline() != defaultFormat->font().overline() ) {
+ if (font().overline())
+ textDecoration += QLatin1String(" overline");
+ else
+ none = true;
+ }
+ if ( font().strikeOut() != defaultFormat->font().strikeOut() ) {
+ if (font().strikeOut())
+ textDecoration += QLatin1String(" line-through");
+ else
+ none = true;
+ }
+ if (none && textDecoration.isEmpty())
+ textDecoration = QLatin1String("none");
+ if (!textDecoration.isEmpty())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("text-decoration:") + textDecoration;
+ if (vAlign() != defaultFormat->vAlign()) {
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("vertical-align:");
+ if (vAlign() == Q3TextFormat::AlignSuperScript)
+ s += QLatin1String("super");
+ else if (vAlign() == Q3TextFormat::AlignSubScript)
+ s += QLatin1String("sub");
+ else
+ s += QLatin1String("normal");
+ }
+ if (color().rgb() != defaultFormat->color().rgb())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("color:") + col.name();
+ if (!s.isEmpty())
+ tag += QLatin1String("<span style=\"") + s + QLatin1String("\">");
+ }
+
+ return tag;
+}
+
+QString Q3TextFormat::makeFormatEndTags(Q3TextFormat* defaultFormat, const QString& anchorHref) const
+{
+ QString tag;
+ if (font().family() != defaultFormat->font().family()
+ || font().pointSize() != defaultFormat->font().pointSize()
+ || font().weight() != defaultFormat->font().weight()
+ || font().italic() != defaultFormat->font().italic()
+ || font().underline() != defaultFormat->font().underline()
+ || font().strikeOut() != defaultFormat->font().strikeOut()
+ || vAlign() != defaultFormat->vAlign()
+ || color().rgb() != defaultFormat->color().rgb())
+ tag += QLatin1String("</span>");
+ if (!anchorHref.isEmpty())
+ tag += QLatin1String("</a>");
+ return tag;
+}
+
+Q3TextFormat Q3TextFormat::makeTextFormat(const Q3StyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor) const
+{
+ Q3TextFormat format(*this);
+ if (!style)
+ return format;
+
+ if (!style->isAnchor() && style->color().isValid()) {
+ // the style is not an anchor and defines a color.
+ // It might be used inside an anchor and it should
+ // override the link color.
+ format.linkColor = false;
+ }
+ switch (style->verticalAlignment()) {
+ case Q3StyleSheetItem::VAlignBaseline:
+ format.setVAlign(Q3TextFormat::AlignNormal);
+ break;
+ case Q3StyleSheetItem::VAlignSuper:
+ format.setVAlign(Q3TextFormat::AlignSuperScript);
+ break;
+ case Q3StyleSheetItem::VAlignSub:
+ format.setVAlign(Q3TextFormat::AlignSubScript);
+ break;
+ }
+
+ if (style->fontWeight() != Q3StyleSheetItem::Undefined)
+ format.fn.setWeight(style->fontWeight());
+ if (style->fontSize() != Q3StyleSheetItem::Undefined) {
+ format.fn.setPointSize(style->fontSize());
+ } else if (style->logicalFontSize() != Q3StyleSheetItem::Undefined) {
+ format.logicalFontSize = style->logicalFontSize();
+ if (format.usePixelSizes)
+ format.fn.setPixelSize(format.stdSize);
+ else
+ format.fn.setPointSize(format.stdSize);
+ style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
+ } else if (style->logicalFontSizeStep()) {
+ format.logicalFontSize += style->logicalFontSizeStep();
+ if (format.usePixelSizes)
+ format.fn.setPixelSize(format.stdSize);
+ else
+ format.fn.setPointSize(format.stdSize);
+ style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
+ }
+ if (!style->fontFamily().isEmpty())
+ format.fn.setFamily(style->fontFamily());
+ if (style->color().isValid())
+ format.col = style->color();
+ if (style->definesFontItalic())
+ format.fn.setItalic(style->fontItalic());
+ if (style->definesFontUnderline())
+ format.fn.setUnderline(style->fontUnderline());
+ if (style->definesFontStrikeOut())
+ format.fn.setStrikeOut(style->fontStrikeOut());
+
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+
+ if (style->name() == QLatin1String("font")) {
+ it = attr.find(QLatin1String("color"));
+ if (it != end && ! (*it).isEmpty()){
+ format.col.setNamedColor(*it);
+ format.linkColor = false;
+ }
+ it = attr.find(QLatin1String("face"));
+ if (it != end) {
+ QString family = (*it).section(QLatin1Char(','), 0, 0);
+ if (family.size())
+ format.fn.setFamily(family);
+ }
+ it = attr.find(QLatin1String("size"));
+ if (it != end) {
+ QString a = *it;
+ int n = a.toInt();
+ if (a[0] == QLatin1Char('+') || a[0] == QLatin1Char('-'))
+ n += 3;
+ format.logicalFontSize = n;
+ if (format.usePixelSizes)
+ format.fn.setPixelSize(format.stdSize);
+ else
+ format.fn.setPointSize(format.stdSize);
+ style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
+ }
+ }
+
+ it = attr.find(QLatin1String("style"));
+ if (it != end) {
+ QString a = *it;
+ int count = a.count(QLatin1Char(';'))+1;
+ for (int s = 0; s < count; s++) {
+ QString style = a.section(QLatin1Char(';'), s, s);
+ if (style.startsWith(QLatin1String("font-size:")) && style.endsWith(QLatin1String("pt"))) {
+ format.logicalFontSize = 0;
+ int size = int(scaleFontsFactor * style.mid(10, style.length() - 12).toDouble());
+ format.setPointSize(size);
+ } else if (style.startsWith(QLatin1String("font-style:"))) {
+ QString s = style.mid(11).trimmed();
+ if (s == QLatin1String("normal"))
+ format.fn.setItalic(false);
+ else if (s == QLatin1String("italic") || s == QLatin1String("oblique"))
+ format.fn.setItalic(true);
+ } else if (style.startsWith(QLatin1String("font-weight:"))) {
+ QString s = style.mid(12);
+ bool ok = true;
+ int n = s.toInt(&ok);
+ if (ok)
+ format.fn.setWeight(n/8);
+ } else if (style.startsWith(QLatin1String("font-family:"))) {
+ QString family = style.mid(12).section(QLatin1Char(','),0,0);
+ family.replace(QLatin1Char('\"'), QLatin1Char(' '));
+ family.replace(QLatin1Char('\''), QLatin1Char(' '));
+ family = family.trimmed();
+ format.fn.setFamily(family);
+ } else if (style.startsWith(QLatin1String("text-decoration:"))) {
+ QString s = style.mid( 16 );
+ format.fn.setOverline(s.contains(QLatin1String("overline")));
+ format.fn.setStrikeOut(s.contains(QLatin1String("line-through")));
+ format.fn.setUnderline(s.contains(QLatin1String("underline")));
+ } else if (style.startsWith(QLatin1String("vertical-align:"))) {
+ QString s = style.mid(15).trimmed();
+ if (s == QLatin1String("sub"))
+ format.setVAlign(Q3TextFormat::AlignSubScript);
+ else if (s == QLatin1String("super"))
+ format.setVAlign(Q3TextFormat::AlignSuperScript);
+ else
+ format.setVAlign(Q3TextFormat::AlignNormal);
+ } else if (style.startsWith(QLatin1String("color:"))) {
+ format.col.setNamedColor(style.mid(6));
+ format.linkColor = false;
+ }
+ }
+ }
+
+ format.update();
+ return format;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+
+struct QPixmapInt
+{
+ QPixmapInt() : ref(0) {}
+ QPixmap pm;
+ int ref;
+ Q_DUMMY_COMPARISON_OPERATOR(QPixmapInt)
+};
+
+static QMap<QString, QPixmapInt> *pixmap_map = 0;
+
+Q3TextImage::Q3TextImage(Q3TextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ Q3MimeSourceFactory &factory)
+ : Q3TextCustomItem(p)
+{
+ width = height = 0;
+
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+ it = attr.find(QLatin1String("width"));
+ if (it != end)
+ width = (*it).toInt();
+ it = attr.find(QLatin1String("height"));
+ if (it != end)
+ height = (*it).toInt();
+
+ reg = 0;
+ QString imageName = attr[QLatin1String("src")];
+
+ if (imageName.size() == 0)
+ imageName = attr[QLatin1String("source")];
+
+ if (!imageName.isEmpty()) {
+ imgId = QString::fromLatin1("%1,%2,%3,%4").arg(imageName).arg(width).arg(height).arg((quintptr)&factory);
+ if (!pixmap_map)
+ pixmap_map = new QMap<QString, QPixmapInt>;
+ if (pixmap_map->contains(imgId)) {
+ QPixmapInt& pmi = pixmap_map->operator[](imgId);
+ pm = pmi.pm;
+ pmi.ref++;
+ width = pm.width();
+ height = pm.height();
+ } else {
+ QImage img;
+ const QMimeSource* m =
+ factory.data(imageName, context);
+ if (!m) {
+ qCritical("Q3TextImage: no mimesource for %s", imageName.latin1());
+ }
+ else {
+ if (!Q3ImageDrag::decode(m, img)) {
+ qCritical("Q3TextImage: cannot decode %s", imageName.latin1());
+ }
+ }
+
+ if (!img.isNull()) {
+ if (width == 0) {
+ width = img.width();
+ if (height != 0) {
+ width = img.width() * height / img.height();
+ }
+ }
+ if (height == 0) {
+ height = img.height();
+ if (width != img.width()) {
+ height = img.height() * width / img.width();
+ }
+ }
+ if (img.width() != width || img.height() != height){
+#ifndef QT_NO_IMAGE_SMOOTHSCALE
+ img = img.smoothScale(width, height);
+#endif
+ width = img.width();
+ height = img.height();
+ }
+ pm.convertFromImage(img);
+ }
+ if (!pm.isNull()) {
+ QPixmapInt& pmi = pixmap_map->operator[](imgId);
+ pmi.pm = pm;
+ pmi.ref++;
+ }
+ }
+ if (pm.hasAlphaChannel()) {
+ QRegion mask(pm.mask());
+ QRegion all(0, 0, pm.width(), pm.height());
+ reg = new QRegion(all.subtracted(mask));
+ }
+ }
+
+ if (pm.isNull() && (width*height)==0)
+ width = height = 50;
+
+ place = PlaceInline;
+ if (attr[QLatin1String("align")] == QLatin1String("left"))
+ place = PlaceLeft;
+ else if (attr[QLatin1String("align")] == QLatin1String("right"))
+ place = PlaceRight;
+
+ tmpwidth = width;
+ tmpheight = height;
+
+ attributes = attr;
+}
+
+Q3TextImage::~Q3TextImage()
+{
+ if (pixmap_map && pixmap_map->contains(imgId)) {
+ QPixmapInt& pmi = pixmap_map->operator[](imgId);
+ pmi.ref--;
+ if (!pmi.ref) {
+ pixmap_map->remove(imgId);
+ if (pixmap_map->isEmpty()) {
+ delete pixmap_map;
+ pixmap_map = 0;
+ }
+ }
+ }
+ delete reg;
+}
+
+QString Q3TextImage::richText() const
+{
+ QString s;
+ s += QLatin1String("<img ");
+ QMap<QString, QString>::ConstIterator it = attributes.begin();
+ for (; it != attributes.end(); ++it) {
+ s += it.key() + QLatin1Char('=');
+ if ((*it).contains(QLatin1Char(' ')))
+ s += QLatin1Char('\"') + *it + QLatin1String("\" ");
+ else
+ s += *it + QLatin1Char(' ');
+ }
+ s += QLatin1Char('>');
+ return s;
+}
+
+void Q3TextImage::adjustToPainter(QPainter* p)
+{
+ width = scale(tmpwidth, p);
+ height = scale(tmpheight, p);
+}
+
+#if !defined(Q_WS_X11)
+static QPixmap *qrt_selection = 0;
+static Q3SingleCleanupHandler<QPixmap> qrt_cleanup_pixmap;
+static void qrt_createSelectionPixmap(const QPalette &pal)
+{
+ qrt_selection = new QPixmap(2, 2);
+ qrt_cleanup_pixmap.set(&qrt_selection);
+ qrt_selection->fill(Qt::color0);
+ QBitmap m(2, 2);
+ m.fill(Qt::color1);
+ QPainter p(&m);
+ p.setPen(Qt::color0);
+ for (int j = 0; j < 2; ++j) {
+ p.drawPoint(j % 2, j);
+ }
+ p.end();
+ qrt_selection->setMask(m);
+ qrt_selection->fill(pal.highlight().color());
+}
+#endif
+
+void Q3TextImage::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected)
+{
+ if (placement() != PlaceInline) {
+ x = xpos;
+ y = ypos;
+ }
+
+ if (pm.isNull()) {
+ p->fillRect(x , y, width, height, pal.dark());
+ return;
+ }
+
+ if (is_printer(p)) {
+ p->drawPixmap(QRect(x, y, width, height), pm);
+ return;
+ }
+
+ if (placement() != PlaceInline && !QRect(xpos, ypos, width, height).intersects(QRect(cx, cy, cw, ch)))
+ return;
+
+ if (placement() == PlaceInline)
+ p->drawPixmap(x , y, pm);
+ else
+ p->drawPixmap(cx , cy, pm, cx - x, cy - y, cw, ch);
+
+ if (selected && placement() == PlaceInline && is_printer(p)) {
+#if defined(Q_WS_X11)
+ p->fillRect(QRect(QPoint(x, y), pm.size()), QBrush(pal.highlight(),
+ Qt::Dense4Pattern));
+#else // in WIN32 Qt::Dense4Pattern doesn't work correctly (transparency problem), so work around it
+ if (!qrt_selection)
+ qrt_createSelectionPixmap(pal);
+ p->drawTiledPixmap(x, y, pm.width(), pm.height(), *qrt_selection);
+#endif
+ }
+}
+
+void Q3TextHorizontalLine::adjustToPainter(QPainter* p)
+{
+ height = scale(tmpheight, p);
+}
+
+
+Q3TextHorizontalLine::Q3TextHorizontalLine(Q3TextDocument *p, const QMap<QString, QString> &attr,
+ const QString &,
+ Q3MimeSourceFactory &)
+ : Q3TextCustomItem(p)
+{
+ height = tmpheight = 8;
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+ it = attr.find(QLatin1String("color"));
+ if (it != end)
+ color = QColor(*it);
+ shade = attr.find(QLatin1String("noshade")) == end;
+}
+
+Q3TextHorizontalLine::~Q3TextHorizontalLine()
+{
+}
+
+QString Q3TextHorizontalLine::richText() const
+{
+ return QLatin1String("<hr>");
+}
+
+void Q3TextHorizontalLine::draw(QPainter* p, int x, int y, int , int , int , int ,
+ const QPalette& pal, bool selected)
+{
+ QRect r(x, y, width, height);
+ if (is_printer(p) || !shade) {
+ QPen oldPen = p->pen();
+ if (!color.isValid())
+ p->setPen(QPen(pal.text().color(), is_printer(p) ? height/8 : qMax(2, height/4)));
+ else
+ p->setPen(QPen(color, is_printer(p) ? height/8 : qMax(2, height/4)));
+ p->drawLine(r.left()-1, y + height / 2, r.right() + 1, y + height / 2);
+ p->setPen(oldPen);
+ } else {
+ if (selected)
+ p->fillRect(r, pal.highlight());
+ QPalette pal2(pal);
+ if (color.isValid())
+ pal2.setColor(pal2.currentColorGroup(), QPalette::Dark, color);
+ qDrawShadeLine(p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, pal2,
+ true, height / 8);
+ }
+}
+#endif //QT_NO_TEXTCUSTOMITEM
+
+/*****************************************************************/
+// Small set of utility functions to make the parser a bit simpler
+//
+
+bool Q3TextDocument::hasPrefix(const QChar* doc, int length, int pos, QChar c)
+{
+ if (pos + 1 > length)
+ return false;
+ return doc[pos].toLower() == c.toLower();
+}
+
+bool Q3TextDocument::hasPrefix(const QChar* doc, int length, int pos, const QString& s)
+{
+ if (pos + (int) s.length() > length)
+ return false;
+ for (int i = 0; i < (int)s.length(); i++) {
+ if (doc[pos + i].toLower() != s[i].toLower())
+ return false;
+ }
+ return true;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+static bool qt_is_cell_in_use(QList<Q3TextTableCell *>& cells, int row, int col)
+{
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (row >= c->row() && row < c->row() + c->rowspan()
+ && col >= c->column() && col < c->column() + c->colspan())
+ return true;
+ }
+ return false;
+}
+
+Q3TextCustomItem* Q3TextDocument::parseTable(const QMap<QString, QString> &attr, const Q3TextFormat &fmt,
+ const QChar* doc, int length, int& pos, Q3TextParagraph *curpar)
+{
+
+ Q3TextTable* table = new Q3TextTable(this, attr);
+ int row = -1;
+ int col = -1;
+
+ QString rowbgcolor;
+ QString rowalign;
+ QString tablebgcolor = attr[QLatin1String("bgcolor")];
+
+ QList<Q3TextTableCell *> multicells;
+
+ QString tagname;
+ (void) eatSpace(doc, length, pos);
+ while (pos < length) {
+ if (hasPrefix(doc, length, pos, QLatin1Char('<'))){
+ if (hasPrefix(doc, length, pos+1, QLatin1Char('/'))) {
+ tagname = parseCloseTag(doc, length, pos);
+ if (tagname == QLatin1String("table")) {
+ return table;
+ }
+ } else {
+ QMap<QString, QString> attr2;
+ bool emptyTag = false;
+ tagname = parseOpenTag(doc, length, pos, attr2, emptyTag);
+ if (tagname == QLatin1String("tr")) {
+ rowbgcolor = attr2[QLatin1String("bgcolor")];
+ rowalign = attr2[QLatin1String("align")];
+ row++;
+ col = -1;
+ }
+ else if (tagname == QLatin1String("td") || tagname == QLatin1String("th")) {
+ col++;
+ while (qt_is_cell_in_use(multicells, row, col)) {
+ col++;
+ }
+
+ if (row >= 0 && col >= 0) {
+ const Q3StyleSheetItem* s = sheet_->item(tagname);
+ if (!attr2.contains(QLatin1String("bgcolor"))) {
+ if (!rowbgcolor.isEmpty())
+ attr2[QLatin1String("bgcolor")] = rowbgcolor;
+ else if (!tablebgcolor.isEmpty())
+ attr2[QLatin1String("bgcolor")] = tablebgcolor;
+ }
+ if (!attr2.contains(QLatin1String("align"))) {
+ if (!rowalign.isEmpty())
+ attr2[QLatin1String("align")] = rowalign;
+ }
+
+ // extract the cell contents
+ int end = pos;
+ while (end < length
+ && !hasPrefix(doc, length, end, QLatin1String("</td"))
+ && !hasPrefix(doc, length, end, QLatin1String("<td"))
+ && !hasPrefix(doc, length, end, QLatin1String("</th"))
+ && !hasPrefix(doc, length, end, QLatin1String("<th"))
+ && !hasPrefix(doc, length, end, QLatin1String("<td"))
+ && !hasPrefix(doc, length, end, QLatin1String("</tr"))
+ && !hasPrefix(doc, length, end, QLatin1String("<tr"))
+ && !hasPrefix(doc, length, end, QLatin1String("</table"))) {
+ if (hasPrefix(doc, length, end, QLatin1String("<table"))) { // nested table
+ int nested = 1;
+ ++end;
+ while (end < length && nested != 0) {
+ if (hasPrefix(doc, length, end, QLatin1String("</table")))
+ nested--;
+ if (hasPrefix(doc, length, end, QLatin1String("<table")))
+ nested++;
+ end++;
+ }
+ }
+ end++;
+ }
+ Q3TextTableCell* cell = new Q3TextTableCell(table, row, col,
+ attr2, s, fmt.makeTextFormat(s, attr2, scaleFontsFactor),
+ contxt, *factory_, sheet_,
+ QString::fromRawData(doc + pos, end - pos));
+ cell->richText()->parentPar = curpar;
+ if (cell->colspan() > 1 || cell->rowspan() > 1)
+ multicells.append(cell);
+ col += cell->colspan()-1;
+ pos = end;
+ }
+ }
+ }
+
+ } else {
+ ++pos;
+ }
+ }
+ return table;
+}
+#endif // QT_NO_TEXTCUSTOMITEM
+
+bool Q3TextDocument::eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp)
+{
+ int old_pos = pos;
+ while (pos < length && doc[pos].isSpace() && (includeNbsp || (doc[pos] != QChar(QChar::nbsp))))
+ pos++;
+ return old_pos < pos;
+}
+
+bool Q3TextDocument::eat(const QChar* doc, int length, int& pos, QChar c)
+{
+ bool ok = pos < length && doc[pos] == c;
+ if (ok)
+ pos++;
+ return ok;
+}
+/*****************************************************************/
+
+struct Entity {
+ const char * name;
+ Q_UINT16 code;
+};
+
+static const Entity entitylist [] = {
+ { "AElig", 0x00c6 },
+ { "Aacute", 0x00c1 },
+ { "Acirc", 0x00c2 },
+ { "Agrave", 0x00c0 },
+ { "Alpha", 0x0391 },
+ { "AMP", 38 },
+ { "Aring", 0x00c5 },
+ { "Atilde", 0x00c3 },
+ { "Auml", 0x00c4 },
+ { "Beta", 0x0392 },
+ { "Ccedil", 0x00c7 },
+ { "Chi", 0x03a7 },
+ { "Dagger", 0x2021 },
+ { "Delta", 0x0394 },
+ { "ETH", 0x00d0 },
+ { "Eacute", 0x00c9 },
+ { "Ecirc", 0x00ca },
+ { "Egrave", 0x00c8 },
+ { "Epsilon", 0x0395 },
+ { "Eta", 0x0397 },
+ { "Euml", 0x00cb },
+ { "Gamma", 0x0393 },
+ { "GT", 62 },
+ { "Iacute", 0x00cd },
+ { "Icirc", 0x00ce },
+ { "Igrave", 0x00cc },
+ { "Iota", 0x0399 },
+ { "Iuml", 0x00cf },
+ { "Kappa", 0x039a },
+ { "Lambda", 0x039b },
+ { "LT", 60 },
+ { "Mu", 0x039c },
+ { "Ntilde", 0x00d1 },
+ { "Nu", 0x039d },
+ { "OElig", 0x0152 },
+ { "Oacute", 0x00d3 },
+ { "Ocirc", 0x00d4 },
+ { "Ograve", 0x00d2 },
+ { "Omega", 0x03a9 },
+ { "Omicron", 0x039f },
+ { "Oslash", 0x00d8 },
+ { "Otilde", 0x00d5 },
+ { "Ouml", 0x00d6 },
+ { "Phi", 0x03a6 },
+ { "Pi", 0x03a0 },
+ { "Prime", 0x2033 },
+ { "Psi", 0x03a8 },
+ { "QUOT", 34 },
+ { "Rho", 0x03a1 },
+ { "Scaron", 0x0160 },
+ { "Sigma", 0x03a3 },
+ { "THORN", 0x00de },
+ { "Tau", 0x03a4 },
+ { "Theta", 0x0398 },
+ { "Uacute", 0x00da },
+ { "Ucirc", 0x00db },
+ { "Ugrave", 0x00d9 },
+ { "Upsilon", 0x03a5 },
+ { "Uuml", 0x00dc },
+ { "Xi", 0x039e },
+ { "Yacute", 0x00dd },
+ { "Yuml", 0x0178 },
+ { "Zeta", 0x0396 },
+ { "aacute", 0x00e1 },
+ { "acirc", 0x00e2 },
+ { "acute", 0x00b4 },
+ { "aelig", 0x00e6 },
+ { "agrave", 0x00e0 },
+ { "alefsym", 0x2135 },
+ { "alpha", 0x03b1 },
+ { "amp", 38 },
+ { "and", 0x22a5 },
+ { "ang", 0x2220 },
+ { "apos", 0x0027 },
+ { "aring", 0x00e5 },
+ { "asymp", 0x2248 },
+ { "atilde", 0x00e3 },
+ { "auml", 0x00e4 },
+ { "bdquo", 0x201e },
+ { "beta", 0x03b2 },
+ { "brvbar", 0x00a6 },
+ { "bull", 0x2022 },
+ { "cap", 0x2229 },
+ { "ccedil", 0x00e7 },
+ { "cedil", 0x00b8 },
+ { "cent", 0x00a2 },
+ { "chi", 0x03c7 },
+ { "circ", 0x02c6 },
+ { "clubs", 0x2663 },
+ { "cong", 0x2245 },
+ { "copy", 0x00a9 },
+ { "crarr", 0x21b5 },
+ { "cup", 0x222a },
+ { "cur" "ren", 0x00a4 },
+ { "dArr", 0x21d3 },
+ { "dagger", 0x2020 },
+ { "darr", 0x2193 },
+ { "deg", 0x00b0 },
+ { "delta", 0x03b4 },
+ { "diams", 0x2666 },
+ { "divide", 0x00f7 },
+ { "eacute", 0x00e9 },
+ { "ecirc", 0x00ea },
+ { "egrave", 0x00e8 },
+ { "empty", 0x2205 },
+ { "emsp", 0x2003 },
+ { "ensp", 0x2002 },
+ { "epsilon", 0x03b5 },
+ { "equiv", 0x2261 },
+ { "eta", 0x03b7 },
+ { "eth", 0x00f0 },
+ { "euml", 0x00eb },
+ { "euro", 0x20ac },
+ { "exist", 0x2203 },
+ { "fnof", 0x0192 },
+ { "forall", 0x2200 },
+ { "frac12", 0x00bd },
+ { "frac14", 0x00bc },
+ { "frac34", 0x00be },
+ { "frasl", 0x2044 },
+ { "gamma", 0x03b3 },
+ { "ge", 0x2265 },
+ { "gt", 62 },
+ { "hArr", 0x21d4 },
+ { "harr", 0x2194 },
+ { "hearts", 0x2665 },
+ { "hellip", 0x2026 },
+ { "iacute", 0x00ed },
+ { "icirc", 0x00ee },
+ { "iexcl", 0x00a1 },
+ { "igrave", 0x00ec },
+ { "image", 0x2111 },
+ { "infin", 0x221e },
+ { "int", 0x222b },
+ { "iota", 0x03b9 },
+ { "iquest", 0x00bf },
+ { "isin", 0x2208 },
+ { "iuml", 0x00ef },
+ { "kappa", 0x03ba },
+ { "lArr", 0x21d0 },
+ { "lambda", 0x03bb },
+ { "lang", 0x2329 },
+ { "laquo", 0x00ab },
+ { "larr", 0x2190 },
+ { "lceil", 0x2308 },
+ { "ldquo", 0x201c },
+ { "le", 0x2264 },
+ { "lfloor", 0x230a },
+ { "lowast", 0x2217 },
+ { "loz", 0x25ca },
+ { "lrm", 0x200e },
+ { "lsaquo", 0x2039 },
+ { "lsquo", 0x2018 },
+ { "lt", 60 },
+ { "macr", 0x00af },
+ { "mdash", 0x2014 },
+ { "micro", 0x00b5 },
+ { "middot", 0x00b7 },
+ { "minus", 0x2212 },
+ { "mu", 0x03bc },
+ { "nabla", 0x2207 },
+ { "nbsp", 0x00a0 },
+ { "ndash", 0x2013 },
+ { "ne", 0x2260 },
+ { "ni", 0x220b },
+ { "not", 0x00ac },
+ { "notin", 0x2209 },
+ { "nsub", 0x2284 },
+ { "ntilde", 0x00f1 },
+ { "nu", 0x03bd },
+ { "oacute", 0x00f3 },
+ { "ocirc", 0x00f4 },
+ { "oelig", 0x0153 },
+ { "ograve", 0x00f2 },
+ { "oline", 0x203e },
+ { "omega", 0x03c9 },
+ { "omicron", 0x03bf },
+ { "oplus", 0x2295 },
+ { "or", 0x22a6 },
+ { "ordf", 0x00aa },
+ { "ordm", 0x00ba },
+ { "oslash", 0x00f8 },
+ { "otilde", 0x00f5 },
+ { "otimes", 0x2297 },
+ { "ouml", 0x00f6 },
+ { "para", 0x00b6 },
+ { "part", 0x2202 },
+ { "percnt", 0x0025 },
+ { "permil", 0x2030 },
+ { "perp", 0x22a5 },
+ { "phi", 0x03c6 },
+ { "pi", 0x03c0 },
+ { "piv", 0x03d6 },
+ { "plusmn", 0x00b1 },
+ { "pound", 0x00a3 },
+ { "prime", 0x2032 },
+ { "prod", 0x220f },
+ { "prop", 0x221d },
+ { "psi", 0x03c8 },
+ { "quot", 34 },
+ { "rArr", 0x21d2 },
+ { "radic", 0x221a },
+ { "rang", 0x232a },
+ { "raquo", 0x00bb },
+ { "rarr", 0x2192 },
+ { "rceil", 0x2309 },
+ { "rdquo", 0x201d },
+ { "real", 0x211c },
+ { "reg", 0x00ae },
+ { "rfloor", 0x230b },
+ { "rho", 0x03c1 },
+ { "rlm", 0x200f },
+ { "rsaquo", 0x203a },
+ { "rsquo", 0x2019 },
+ { "sbquo", 0x201a },
+ { "scaron", 0x0161 },
+ { "sdot", 0x22c5 },
+ { "sect", 0x00a7 },
+ { "shy", 0x00ad },
+ { "sigma", 0x03c3 },
+ { "sigmaf", 0x03c2 },
+ { "sim", 0x223c },
+ { "spades", 0x2660 },
+ { "sub", 0x2282 },
+ { "sube", 0x2286 },
+ { "sum", 0x2211 },
+ { "sup1", 0x00b9 },
+ { "sup2", 0x00b2 },
+ { "sup3", 0x00b3 },
+ { "sup", 0x2283 },
+ { "supe", 0x2287 },
+ { "szlig", 0x00df },
+ { "tau", 0x03c4 },
+ { "there4", 0x2234 },
+ { "theta", 0x03b8 },
+ { "thetasym", 0x03d1 },
+ { "thinsp", 0x2009 },
+ { "thorn", 0x00fe },
+ { "tilde", 0x02dc },
+ { "times", 0x00d7 },
+ { "trade", 0x2122 },
+ { "uArr", 0x21d1 },
+ { "uacute", 0x00fa },
+ { "uarr", 0x2191 },
+ { "ucirc", 0x00fb },
+ { "ugrave", 0x00f9 },
+ { "uml", 0x00a8 },
+ { "upsih", 0x03d2 },
+ { "upsilon", 0x03c5 },
+ { "uuml", 0x00fc },
+ { "weierp", 0x2118 },
+ { "xi", 0x03be },
+ { "yacute", 0x00fd },
+ { "yen", 0x00a5 },
+ { "yuml", 0x00ff },
+ { "zeta", 0x03b6 },
+ { "zwj", 0x200d },
+ { "zwnj", 0x200c },
+ { "", 0x0000 }
+};
+
+
+
+
+
+static QMap<QByteArray, QChar> *html_map = 0;
+static void qt_cleanup_html_map()
+{
+ delete html_map;
+ html_map = 0;
+}
+
+static QMap<QByteArray, QChar> *htmlMap()
+{
+ if (!html_map) {
+ html_map = new QMap<QByteArray, QChar>;
+ qAddPostRoutine(qt_cleanup_html_map);
+
+ const Entity *ent = entitylist;
+ while(ent->code) {
+ html_map->insert(QByteArray(ent->name), QChar(ent->code));
+ ent++;
+ }
+ }
+ return html_map;
+}
+
+QChar Q3TextDocument::parseHTMLSpecialChar(const QChar* doc, int length, int& pos)
+{
+ QString s;
+ pos++;
+ int recoverpos = pos;
+ while (pos < length && doc[pos] != QLatin1Char(';') && !doc[pos].isSpace() && pos < recoverpos + 8) {
+ s += doc[pos];
+ pos++;
+ }
+ if (doc[pos] != QLatin1Char(';') && !doc[pos].isSpace()) {
+ pos = recoverpos;
+ return QLatin1Char('&');
+ }
+ pos++;
+
+ if (s.length() > 1 && s[0] == QLatin1Char('#')) {
+ int off = 1;
+ int base = 10;
+ if (s[1] == QLatin1Char('x')) {
+ off = 2;
+ base = 16;
+ }
+ bool ok;
+ int num = s.mid(off).toInt(&ok, base);
+ if (num == 151) // ### hack for designer manual
+ return QLatin1Char('-');
+ return num;
+ }
+
+ QMap<QByteArray, QChar>::Iterator it = htmlMap()->find(s.toLatin1());
+ if (it != htmlMap()->end()) {
+ return *it;
+ }
+
+ pos = recoverpos;
+ return QLatin1Char('&');
+}
+
+QString Q3TextDocument::parseWord(const QChar* doc, int length, int& pos, bool lower)
+{
+ QString s;
+
+ if (doc[pos] == QLatin1Char('"')) {
+ pos++;
+ while (pos < length && doc[pos] != QLatin1Char('"')) {
+ if (doc[pos] == QLatin1Char('&')) {
+ s += parseHTMLSpecialChar(doc, length, pos);
+ } else {
+ s += doc[pos];
+ pos++;
+ }
+ }
+ eat(doc, length, pos, QLatin1Char('"'));
+ } else if (doc[pos] == QLatin1Char('\'')) {
+ pos++;
+ while (pos < length && doc[pos] != QLatin1Char('\'')) {
+ s += doc[pos];
+ pos++;
+ }
+ eat(doc, length, pos, QLatin1Char('\''));
+ } else {
+ static QString term = QString::fromLatin1("/>");
+ while (pos < length
+ && doc[pos] != QLatin1Char('>')
+ && !hasPrefix(doc, length, pos, term)
+ && doc[pos] != QLatin1Char('<')
+ && doc[pos] != QLatin1Char('=')
+ && !doc[pos].isSpace())
+ {
+ if (doc[pos] == QLatin1Char('&')) {
+ s += parseHTMLSpecialChar(doc, length, pos);
+ } else {
+ s += doc[pos];
+ pos++;
+ }
+ }
+ if (lower)
+ s = s.toLower();
+ }
+ return s;
+}
+
+QChar Q3TextDocument::parseChar(const QChar* doc, int length, int& pos, Q3StyleSheetItem::WhiteSpaceMode wsm)
+{
+ if (pos >= length)
+ return QChar::null;
+
+ QChar c = doc[pos++];
+
+ if (c == QLatin1Char('<'))
+ return QChar::null;
+
+ if (c.isSpace() && c != QChar(QChar::nbsp)) {
+ if (wsm == Q3StyleSheetItem::WhiteSpacePre) {
+ if (c == QLatin1Char('\n'))
+ return QChar::LineSeparator;
+ else
+ return c;
+ } else { // non-pre mode: collapse whitespace except nbsp
+ while (pos< length &&
+ doc[pos].isSpace() && doc[pos] != QChar(QChar::nbsp))
+ pos++;
+ return QLatin1Char(' ');
+ }
+ }
+ else if (c == QLatin1Char('&'))
+ return parseHTMLSpecialChar(doc, length, --pos);
+ else
+ return c;
+}
+
+QString Q3TextDocument::parseOpenTag(const QChar* doc, int length, int& pos,
+ QMap<QString, QString> &attr, bool& emptyTag)
+{
+ emptyTag = false;
+ pos++;
+ if (hasPrefix(doc, length, pos, QLatin1Char('!'))) {
+ if (hasPrefix(doc, length, pos+1, QLatin1String("--"))) {
+ pos += 3;
+ // eat comments
+ QString pref = QString::fromLatin1("-->");
+ while (!hasPrefix(doc, length, pos, pref) && pos < length)
+ pos++;
+ if (hasPrefix(doc, length, pos, pref)) {
+ pos += 3;
+ eatSpace(doc, length, pos, true);
+ }
+ emptyTag = true;
+ return QString();
+ }
+ else {
+ // eat strange internal tags
+ while (!hasPrefix(doc, length, pos, QLatin1Char('>')) && pos < length)
+ pos++;
+ if (hasPrefix(doc, length, pos, QLatin1Char('>'))) {
+ pos++;
+ eatSpace(doc, length, pos, true);
+ }
+ return QString();
+ }
+ }
+
+ QString tag = parseWord(doc, length, pos);
+ eatSpace(doc, length, pos, true);
+ static QString term = QString::fromLatin1("/>");
+ static QString s_TRUE = QString::fromLatin1("TRUE");
+
+ while (doc[pos] != QLatin1Char('>') && ! (emptyTag = hasPrefix(doc, length, pos, term))) {
+ QString key = parseWord(doc, length, pos);
+ eatSpace(doc, length, pos, true);
+ if (key.isEmpty()) {
+ // error recovery
+ while (pos < length && doc[pos] != QLatin1Char('>'))
+ pos++;
+ break;
+ }
+ QString value;
+ if (hasPrefix(doc, length, pos, QLatin1Char('='))){
+ pos++;
+ eatSpace(doc, length, pos);
+ value = parseWord(doc, length, pos, false);
+ }
+ else
+ value = s_TRUE;
+ attr.insert(key.toLower(), value);
+ eatSpace(doc, length, pos, true);
+ }
+
+ if (emptyTag) {
+ eat(doc, length, pos, QLatin1Char('/'));
+ eat(doc, length, pos, QLatin1Char('>'));
+ }
+ else
+ eat(doc, length, pos, QLatin1Char('>'));
+
+ return tag;
+}
+
+QString Q3TextDocument::parseCloseTag(const QChar* doc, int length, int& pos)
+{
+ pos++;
+ pos++;
+ QString tag = parseWord(doc, length, pos);
+ eatSpace(doc, length, pos, true);
+ eat(doc, length, pos, QLatin1Char('>'));
+ return tag;
+}
+
+Q3TextFlow::Q3TextFlow()
+{
+ w = pagesize = 0;
+}
+
+Q3TextFlow::~Q3TextFlow()
+{
+ clear();
+}
+
+void Q3TextFlow::clear()
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ while (!leftItems.isEmpty())
+ delete leftItems.takeFirst();
+ while (!rightItems.isEmpty())
+ delete rightItems.takeFirst();
+#endif
+}
+
+void Q3TextFlow::setWidth(int width)
+{
+ w = width;
+}
+
+int Q3TextFlow::adjustLMargin(int yp, int, int margin, int space)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < leftItems.size(); ++idx) {
+ Q3TextCustomItem* item = leftItems.at(idx);
+ if (item->ypos == -1)
+ continue;
+ if (yp >= item->ypos && yp < item->ypos + item->height)
+ margin = qMax(margin, item->xpos + item->width + space);
+ }
+#endif
+ return margin;
+}
+
+int Q3TextFlow::adjustRMargin(int yp, int, int margin, int space)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < rightItems.size(); ++idx) {
+ Q3TextCustomItem* item = rightItems.at(idx);
+ if (item->ypos == -1)
+ continue;
+ if (yp >= item->ypos && yp < item->ypos + item->height)
+ margin = qMax(margin, w - item->xpos - space);
+ }
+#endif
+ return margin;
+}
+
+
+int Q3TextFlow::adjustFlow(int y, int /*w*/, int h)
+{
+ if (pagesize > 0) { // check pages
+ int yinpage = y % pagesize;
+ if (yinpage <= border_tolerance)
+ return border_tolerance - yinpage;
+ else
+ if (yinpage + h > pagesize - border_tolerance)
+ return (pagesize - yinpage) + border_tolerance;
+ }
+ return 0;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextFlow::unregisterFloatingItem(Q3TextCustomItem* item)
+{
+ leftItems.removeAll(item);
+ rightItems.removeAll(item);
+}
+
+void Q3TextFlow::registerFloatingItem(Q3TextCustomItem* item)
+{
+ if (item->placement() == Q3TextCustomItem::PlaceRight) {
+ if (!rightItems.contains(item))
+ rightItems.append(item);
+ } else if (item->placement() == Q3TextCustomItem::PlaceLeft &&
+ !leftItems.contains(item)) {
+ leftItems.append(item);
+ }
+}
+#endif // QT_NO_TEXTCUSTOMITEM
+
+QRect Q3TextFlow::boundingRect() const
+{
+ QRect br;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < leftItems.size(); ++idx) {
+ Q3TextCustomItem* item = leftItems.at(idx);
+ br = br.united(item->geometry());
+ }
+ for (int idx = 0; idx < rightItems.size(); ++idx) {
+ Q3TextCustomItem* item = rightItems.at(idx);
+ br = br.united(item->geometry());
+ }
+#endif
+ return br;
+}
+
+
+void Q3TextFlow::drawFloatingItems(QPainter* p, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < leftItems.size(); ++idx) {
+ Q3TextCustomItem* item = leftItems.at(idx);
+ if (item->xpos == -1 || item->ypos == -1)
+ continue;
+ item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected);
+ }
+
+ for (int idx = 0; idx < rightItems.size(); ++idx) {
+ Q3TextCustomItem* item = rightItems.at(idx);
+ if (item->xpos == -1 || item->ypos == -1)
+ continue;
+ item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected);
+ }
+#endif
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextCustomItem::pageBreak(int /*y*/ , Q3TextFlow* /*flow*/)
+{
+}
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+Q3TextTable::Q3TextTable(Q3TextDocument *p, const QMap<QString, QString> & attr )
+ : Q3TextCustomItem(p)
+{
+ cellspacing = 2;
+ cellpadding = 1;
+ border = innerborder = 0;
+
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+ it = attr.find(QLatin1String("cellspacing"));
+ if (it != end)
+ cellspacing = (*it).toInt();
+ it = attr.find(QLatin1String("cellpadding"));
+ if (it != end)
+ cellpadding = (*it).toInt();
+ it = attr.find(QLatin1String("border"));
+ if (it != end) {
+ if (*it == QLatin1String("TRUE"))
+ border = 1;
+ else
+ border = (*it).toInt();
+ }
+ us_b = border;
+
+ innerborder = us_ib = border ? 1 : 0;
+
+ if (border)
+ cellspacing += 2;
+
+ us_ib = innerborder;
+ us_cs = cellspacing;
+ us_cp = cellpadding;
+ outerborder = cellspacing + border;
+ us_ob = outerborder;
+ layout = new QGridLayout(1, 1, cellspacing);
+
+ fixwidth = 0;
+ stretch = 0;
+ it = attr.find(QLatin1String("width"));
+ if (it != end) {
+ bool b;
+ QString s(*it);
+ int w = s.toInt(&b);
+ if (b) {
+ fixwidth = w;
+ } else {
+ s = s.trimmed();
+ if (s.length() > 1 && s[(int)s.length()-1] == QLatin1Char('%'))
+ stretch = s.left(s.length()-1).toInt();
+ }
+ }
+ us_fixwidth = fixwidth;
+
+ place = PlaceInline;
+ if (attr[QLatin1String("align")] == QLatin1String("left"))
+ place = PlaceLeft;
+ else if (attr[QLatin1String("align")] == QLatin1String("right"))
+ place = PlaceRight;
+ cachewidth = 0;
+ attributes = attr;
+ pageBreakFor = -1;
+}
+
+Q3TextTable::~Q3TextTable()
+{
+ delete layout;
+}
+
+QString Q3TextTable::richText() const
+{
+ QString s;
+ s = QLatin1String("<table ");
+ QMap<QString, QString>::ConstIterator it = attributes.begin();
+ for (; it != attributes.end(); ++it)
+ s += it.key() + QLatin1Char('=') + *it + QLatin1Char(' ');
+ s += QLatin1String(">\n");
+
+ int lastRow = -1;
+ bool needEnd = false;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ if (lastRow != cell->row()) {
+ if (lastRow != -1)
+ s += QLatin1String("</tr>\n");
+ s += QLatin1String("<tr>");
+ lastRow = cell->row();
+ needEnd = true;
+ }
+ s += QLatin1String("<td");
+ it = cell->attributes.constBegin();
+ for (; it != cell->attributes.constEnd(); ++it)
+ s += QLatin1Char(' ') + it.key() + QLatin1Char('=') + *it;
+ s += QLatin1Char('>');
+ s += cell->richText()->richText();
+ s += QLatin1String("</td>");
+ }
+ if (needEnd)
+ s += QLatin1String("</tr>\n");
+ s += QLatin1String("</table>\n");
+ return s;
+}
+
+void Q3TextTable::adjustToPainter(QPainter* p)
+{
+ cellspacing = scale(us_cs, p);
+ cellpadding = scale(us_cp, p);
+ border = scale(us_b , p);
+ innerborder = scale(us_ib, p);
+ outerborder = scale(us_ob ,p);
+ fixwidth = scale( us_fixwidth, p);
+ width = 0;
+ cachewidth = 0;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ cell->adjustToPainter(p);
+ }
+}
+
+void Q3TextTable::adjustCells(int y , int shift)
+{
+ bool enlarge = false;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ QRect r = cell->geometry();
+ if (y <= r.top()) {
+ r.moveBy(0, shift);
+ cell->setGeometry(r);
+ enlarge = true;
+ } else if (y <= r.bottom()) {
+ r.rBottom() += shift;
+ cell->setGeometry(r);
+ enlarge = true;
+ }
+ }
+ if (enlarge)
+ height += shift;
+}
+
+void Q3TextTable::pageBreak(int yt, Q3TextFlow* flow)
+{
+ if (flow->pageSize() <= 0)
+ return;
+ if (layout && pageBreakFor > 0 && pageBreakFor != yt) {
+ layout->invalidate();
+ int h = layout->heightForWidth(width-2*outerborder);
+ layout->setGeometry(QRect(0, 0, width-2*outerborder, h) );
+ height = layout->geometry().height()+2*outerborder;
+ }
+ pageBreakFor = yt;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ int y = yt + outerborder + cell->geometry().y();
+ int shift = flow->adjustFlow(y - cellspacing, width, cell->richText()->height() + 2*cellspacing);
+ adjustCells(y - outerborder - yt, shift);
+ }
+}
+
+
+void Q3TextTable::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected)
+{
+ if (placement() != PlaceInline) {
+ x = xpos;
+ y = ypos;
+ }
+
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ if ((cx < 0 && cy < 0) ||
+ QRect(cx, cy, cw, ch).intersects(QRect(x + outerborder + cell->geometry().x(),
+ y + outerborder + cell->geometry().y(),
+ cell->geometry().width(),
+ cell->geometry().height()))) {
+ cell->draw(p, x+outerborder, y+outerborder, cx, cy, cw, ch, pal, selected);
+ if (border) {
+ QRect r(x+outerborder+cell->geometry().x() - innerborder,
+ y+outerborder+cell->geometry().y() - innerborder,
+ cell->geometry().width() + 2 * innerborder,
+ cell->geometry().height() + 2 * innerborder);
+ if (is_printer(p)) {
+ QPen oldPen = p->pen();
+ QRect r2 = r;
+ r2.adjust(innerborder/2, innerborder/2, -innerborder/2, -innerborder/2);
+ p->setPen(QPen(pal.text().color(), innerborder));
+ p->drawRect(r2);
+ p->setPen(oldPen);
+ } else {
+ int s = qMax(cellspacing-2*innerborder, 0);
+ if (s) {
+ p->fillRect(r.left()-s, r.top(), s+1, r.height(), pal.button());
+ p->fillRect(r.right(), r.top(), s+1, r.height(), pal.button());
+ p->fillRect(r.left()-s, r.top()-s, r.width()+2*s, s, pal.button());
+ p->fillRect(r.left()-s, r.bottom(), r.width()+2*s, s, pal.button());
+ }
+ qDrawShadePanel(p, r, pal, true, innerborder);
+ }
+ }
+ }
+ }
+ if (border) {
+ QRect r (x, y, width, height);
+ if (is_printer(p)) {
+ QRect r2 = r;
+ r2.adjust(border/2, border/2, -border/2, -border/2);
+ QPen oldPen = p->pen();
+ p->setPen(QPen(pal.text().color(), border));
+ p->drawRect(r2);
+ p->setPen(oldPen);
+ } else {
+ int s = border+qMax(cellspacing-2*innerborder, 0);
+ if (s) {
+ p->fillRect(r.left(), r.top(), s, r.height(), pal.button());
+ p->fillRect(r.right()-s, r.top(), s, r.height(), pal.button());
+ p->fillRect(r.left(), r.top(), r.width(), s, pal.button());
+ p->fillRect(r.left(), r.bottom()-s, r.width(), s, pal.button());
+ }
+ qDrawShadePanel(p, r, pal, false, border);
+ }
+ }
+
+}
+
+int Q3TextTable::minimumWidth() const
+{
+ return qMax(fixwidth, ((layout ? layout->minimumSize().width() : 0) + 2 * outerborder));
+}
+
+void Q3TextTable::resize(int nwidth)
+{
+ if (fixwidth && cachewidth != 0)
+ return;
+ if (nwidth == cachewidth)
+ return;
+
+
+ cachewidth = nwidth;
+ int w = nwidth;
+
+ format(w);
+
+ if (stretch)
+ nwidth = nwidth * stretch / 100;
+
+ width = nwidth;
+ layout->invalidate();
+ int shw = layout->sizeHint().width() + 2*outerborder;
+ int mw = layout->minimumSize().width() + 2*outerborder;
+ if (stretch)
+ width = qMax(mw, nwidth);
+ else
+ width = qMax(mw, qMin(nwidth, shw));
+
+ if (fixwidth)
+ width = fixwidth;
+
+ layout->invalidate();
+ mw = layout->minimumSize().width() + 2*outerborder;
+ width = qMax(width, mw);
+
+ int h = layout->heightForWidth(width-2*outerborder);
+ layout->setGeometry(QRect(0, 0, width-2*outerborder, h) );
+ height = layout->geometry().height()+2*outerborder;
+}
+
+void Q3TextTable::format(int w)
+{
+ for (int i = 0; i < (int)cells.count(); ++i) {
+ Q3TextTableCell *cell = cells.at(i);
+ QRect r = cell->geometry();
+ r.setWidth(w - 2*outerborder);
+ cell->setGeometry(r);
+ }
+}
+
+void Q3TextTable::addCell(Q3TextTableCell* cell)
+{
+ cells.append(cell);
+ layout->addMultiCell(cell, cell->row(), cell->row() + cell->rowspan()-1,
+ cell->column(), cell->column() + cell->colspan()-1);
+}
+
+bool Q3TextTable::enter(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd)
+{
+ currCell.remove(c);
+ if (!atEnd)
+ return next(c, doc, parag, idx, ox, oy);
+ currCell.insert(c, cells.count());
+ return prev(c, doc, parag, idx, ox, oy);
+}
+
+bool Q3TextTable::enterAt(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &pos)
+{
+ currCell.remove(c);
+ int lastCell = -1;
+ int lastY = -1;
+ int i;
+ for (i = 0; i < (int)cells.count(); ++i) {
+ Q3TextTableCell *cell = cells.at(i);
+ if (!cell)
+ continue;
+ QRect r(cell->geometry().x(),
+ cell->geometry().y(),
+ cell->geometry().width() + 2 * innerborder + 2 * outerborder,
+ cell->geometry().height() + 2 * innerborder + 2 * outerborder);
+
+ if (r.left() <= pos.x() && r.right() >= pos.x()) {
+ if (cell->geometry().y() > lastY) {
+ lastCell = i;
+ lastY = cell->geometry().y();
+ }
+ if (r.top() <= pos.y() && r.bottom() >= pos.y()) {
+ currCell.insert(c, i);
+ break;
+ }
+ }
+ }
+ if (i == (int) cells.count())
+ return false; // no cell found
+
+ if (currCell.find(c) == currCell.end()) {
+ if (lastY != -1)
+ currCell.insert(c, lastCell);
+ else
+ return false;
+ }
+
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->firstParagraph();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::next(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
+{
+ int cc = -1;
+ if (currCell.find(c) != currCell.end())
+ cc = *currCell.find(c);
+ if (cc > (int)cells.count() - 1 || cc < 0)
+ cc = -1;
+ currCell.remove(c);
+ currCell.insert(c, ++cc);
+ if (cc >= (int)cells.count()) {
+ currCell.insert(c, 0);
+ Q3TextCustomItem::next(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *cell = cells.first();
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ idx = -1;
+ return true;
+ }
+
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->firstParagraph();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::prev(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
+{
+ int cc = -1;
+ if (currCell.find(c) != currCell.end())
+ cc = *currCell.find(c);
+ if (cc > (int)cells.count() - 1 || cc < 0)
+ cc = cells.count();
+ currCell.remove(c);
+ currCell.insert(c, --cc);
+ if (cc < 0) {
+ currCell.insert(c, 0);
+ Q3TextCustomItem::prev(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *cell = cells.first();
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ idx = -1;
+ return true;
+ }
+
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->lastParagraph();
+ idx = parag->length() - 1;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::down(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
+{
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (cell->row_ == layout->numRows() - 1) {
+ currCell.insert(c, 0);
+ Q3TextCustomItem::down(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *cell = cells.first();
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ idx = -1;
+ return true;
+ }
+
+ int oldRow = cell->row_;
+ int oldCol = cell->col_;
+ if (currCell.find(c) == currCell.end())
+ return false;
+ int cc = *currCell.find(c);
+ for (int i = cc; i < (int)cells.count(); ++i) {
+ cell = cells.at(i);
+ if (cell->row_ > oldRow && cell->col_ == oldCol) {
+ currCell.insert(c, i);
+ break;
+ }
+ }
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->firstParagraph();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::up(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
+{
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (cell->row_ == 0) {
+ currCell.insert(c, 0);
+ Q3TextCustomItem::up(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *cell = cells.first();
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ idx = -1;
+ return true;
+ }
+
+ int oldRow = cell->row_;
+ int oldCol = cell->col_;
+ if (currCell.find(c) == currCell.end())
+ return false;
+ int cc = *currCell.find(c);
+ for (int i = cc; i >= 0; --i) {
+ cell = cells.at(i);
+ if (cell->row_ < oldRow && cell->col_ == oldCol) {
+ currCell.insert(c, i);
+ break;
+ }
+ }
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->lastParagraph();
+ idx = parag->length() - 1;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+Q3TextTableCell::Q3TextTableCell(Q3TextTable* table,
+ int row, int column,
+ const QMap<QString, QString> &attr,
+ const Q3StyleSheetItem* style,
+ const Q3TextFormat& fmt, const QString& context,
+ Q3MimeSourceFactory &factory, Q3StyleSheet *sheet,
+ const QString& doc)
+{
+ cached_width = -1;
+ cached_sizehint = -1;
+
+ maxw = QWIDGETSIZE_MAX;
+ minw = 0;
+
+ parent = table;
+ row_ = row;
+ col_ = column;
+ stretch_ = 0;
+ richtext = new Q3TextDocument(table->parent);
+ richtext->formatCollection()->setPaintDevice(table->parent->formatCollection()->paintDevice());
+ richtext->bodyText = fmt.color();
+ richtext->setTableCell(this);
+
+ QMap<QString,QString>::ConstIterator it, end = attr.end();
+ int halign = style->alignment();
+ if (halign != Q3StyleSheetItem::Undefined)
+ richtext->setAlignment(halign);
+ it = attr.find(QLatin1String("align"));
+ if (it != end && ! (*it).isEmpty()) {
+ QString a = (*it).toLower();
+ if (a == QLatin1String("left"))
+ richtext->setAlignment(Qt::AlignLeft);
+ else if (a == QLatin1String("center"))
+ richtext->setAlignment(Qt::AlignHCenter);
+ else if (a == QLatin1String("right"))
+ richtext->setAlignment(Qt::AlignRight);
+ }
+ align = 0;
+ it = attr.find(QLatin1String("valign"));
+ if (it != end && ! (*it).isEmpty()) {
+ QString va = (*it).toLower();
+ if ( va == QLatin1String("top") )
+ align |= Qt::AlignTop;
+ else if ( va == QLatin1String("center") || va == QLatin1String("middle") )
+ align |= Qt::AlignVCenter;
+ else if (va == QLatin1String("bottom"))
+ align |= Qt::AlignBottom;
+ }
+ richtext->setFormatter(table->parent->formatter());
+ richtext->setUseFormatCollection(table->parent->useFormatCollection());
+ richtext->setMimeSourceFactory(&factory);
+ richtext->setStyleSheet(sheet);
+ richtext->setRichText(doc, context, &fmt);
+ rowspan_ = 1;
+ colspan_ = 1;
+
+ it = attr.find(QLatin1String("colspan"));
+ if (it != end)
+ colspan_ = (*it).toInt();
+ it = attr.find(QLatin1String("rowspan"));
+ if (it != end)
+ rowspan_ = (*it).toInt();
+
+ background = 0;
+ it = attr.find(QLatin1String("bgcolor"));
+ if (it != end) {
+ background = new QBrush(QColor(*it));
+ }
+
+ hasFixedWidth = false;
+ it = attr.find(QLatin1String("width"));
+ if (it != end) {
+ bool b;
+ QString s(*it);
+ int w = s.toInt(&b);
+ if (b) {
+ maxw = w;
+ minw = maxw;
+ hasFixedWidth = true;
+ } else {
+ s = s.trimmed();
+ if (s.length() > 1 && s[(int)s.length()-1] == QLatin1Char('%'))
+ stretch_ = s.left(s.length()-1).toInt();
+ }
+ }
+
+ attributes = attr;
+
+ parent->addCell(this);
+}
+
+Q3TextTableCell::~Q3TextTableCell()
+{
+ delete background;
+ background = 0;
+ delete richtext;
+ richtext = 0;
+}
+
+QSize Q3TextTableCell::sizeHint() const
+{
+ int extra = 2 * (parent->innerborder + parent->cellpadding + border_tolerance);
+ int used = richtext->widthUsed() + extra;
+
+ if (stretch_) {
+ int w = parent->width * stretch_ / 100 - 2*parent->cellspacing - 2*parent->cellpadding;
+ return QSize(qMin(w, maxw), 0).expandedTo(minimumSize());
+ }
+
+ return QSize(used, 0).expandedTo(minimumSize());
+}
+
+QSize Q3TextTableCell::minimumSize() const
+{
+ int extra = 2 * (parent->innerborder + parent->cellpadding + border_tolerance);
+ return QSize(qMax(richtext->minimumWidth() + extra, minw), 0);
+}
+
+QSize Q3TextTableCell::maximumSize() const
+{
+ return QSize(maxw, QWIDGETSIZE_MAX);
+}
+
+Qt::Orientations Q3TextTableCell::expandingDirections() const
+{
+ return Qt::Horizontal | Qt::Vertical;
+}
+
+bool Q3TextTableCell::isEmpty() const
+{
+ return false;
+}
+void Q3TextTableCell::setGeometry(const QRect& r)
+{
+ int extra = 2 * (parent->innerborder + parent->cellpadding);
+ if (r.width() != cached_width)
+ richtext->doLayout(Q3TextFormat::painter(), r.width() - extra);
+ cached_width = r.width();
+ geom = r;
+}
+
+QRect Q3TextTableCell::geometry() const
+{
+ return geom;
+}
+
+bool Q3TextTableCell::hasHeightForWidth() const
+{
+ return true;
+}
+
+int Q3TextTableCell::heightForWidth(int w) const
+{
+ int extra = 2 * (parent->innerborder + parent->cellpadding);
+ w = qMax(minw, w);
+
+ if (cached_width != w) {
+ Q3TextTableCell* that = (Q3TextTableCell*) this;
+ that->richtext->doLayout(Q3TextFormat::painter(), w - extra);
+ that->cached_width = w;
+ }
+ return richtext->height() + extra;
+}
+
+void Q3TextTableCell::adjustToPainter(QPainter* p)
+{
+ Q3TextParagraph *parag = richtext->firstParagraph();
+ while (parag) {
+ parag->adjustToPainter(p);
+ parag = parag->next();
+ }
+}
+
+int Q3TextTableCell::horizontalAlignmentOffset() const
+{
+ return parent->cellpadding;
+}
+
+int Q3TextTableCell::verticalAlignmentOffset() const
+{
+ if ((align & Qt::AlignVCenter) == Qt::AlignVCenter)
+ return (geom.height() - richtext->height()) / 2;
+ else if ((align & Qt::AlignBottom) == Qt::AlignBottom)
+ return geom.height() - parent->cellpadding - richtext->height() ;
+ return parent->cellpadding;
+}
+
+void Q3TextTableCell::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool)
+{
+ if (cached_width != geom.width()) {
+ int extra = 2 * (parent->innerborder + parent->cellpadding);
+ richtext->doLayout(p, geom.width() - extra);
+ cached_width = geom.width();
+ }
+ QPalette pal2(pal);
+ if (background)
+ pal2.setBrush(QPalette::Base, *background);
+ else if (richtext->paper())
+ pal2.setBrush(QPalette::Base, *richtext->paper());
+
+ p->save();
+ p->translate(x + geom.x(), y + geom.y());
+ if (background)
+ p->fillRect(0, 0, geom.width(), geom.height(), *background);
+ else if (richtext->paper())
+ p->fillRect(0, 0, geom.width(), geom.height(), *richtext->paper());
+
+ p->translate(horizontalAlignmentOffset(), verticalAlignmentOffset());
+
+ QRegion r;
+ if (cx >= 0 && cy >= 0)
+ richtext->draw(p, cx - (x + horizontalAlignmentOffset() + geom.x()),
+ cy - (y + geom.y() + verticalAlignmentOffset()),
+ cw, ch, pal2, false, false, 0);
+ else
+ richtext->draw(p, -1, -1, -1, -1, pal2, false, false, 0);
+
+ p->restore();
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3richtext_p.cpp b/src/qt3support/text/q3richtext_p.cpp
new file mode 100644
index 0000000..eed29ea
--- /dev/null
+++ b/src/qt3support/text/q3richtext_p.cpp
@@ -0,0 +1,636 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3richtext_p.h"
+
+#ifndef QT_NO_RICHTEXT
+
+QT_BEGIN_NAMESPACE
+
+Q3TextCommand::~Q3TextCommand() {}
+Q3TextCommand::Commands Q3TextCommand::type() const { return Invalid; }
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+Q3TextCustomItem::~Q3TextCustomItem() {}
+void Q3TextCustomItem::adjustToPainter(QPainter* p){ if (p) width = 0; }
+Q3TextCustomItem::Placement Q3TextCustomItem::placement() const { return PlaceInline; }
+
+bool Q3TextCustomItem::ownLine() const { return false; }
+void Q3TextCustomItem::resize(int nwidth){ width = nwidth; }
+void Q3TextCustomItem::invalidate() {}
+
+bool Q3TextCustomItem::isNested() const { return false; }
+int Q3TextCustomItem::minimumWidth() const { return 0; }
+
+QString Q3TextCustomItem::richText() const { return QString(); }
+
+bool Q3TextCustomItem::enter(Q3TextCursor *, Q3TextDocument*&, Q3TextParagraph *&, int &, int &, int &, bool)
+{
+ return true;
+}
+bool Q3TextCustomItem::enterAt(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &, const QPoint &)
+{
+ return true;
+}
+bool Q3TextCustomItem::next(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+bool Q3TextCustomItem::prev(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+bool Q3TextCustomItem::down(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+bool Q3TextCustomItem::up(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+#endif // QT_NO_TEXTCUSTOMITEM
+
+void Q3TextFlow::setPageSize(int ps) { pagesize = ps; }
+#ifndef QT_NO_TEXTCUSTOMITEM
+bool Q3TextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
+#else
+bool Q3TextFlow::isEmpty() { return true; }
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextTableCell::invalidate() { cached_width = -1; cached_sizehint = -1; }
+
+void Q3TextTable::invalidate() { cachewidth = -1; }
+#endif
+
+Q3TextParagraphData::~Q3TextParagraphData() {}
+void Q3TextParagraphData::join(Q3TextParagraphData *) {}
+
+Q3TextFormatter::~Q3TextFormatter() {}
+void Q3TextFormatter::setWrapEnabled(bool b) { wrapEnabled = b; }
+void Q3TextFormatter::setWrapAtColumn(int c) { wrapColumn = c; }
+
+
+
+int Q3TextCursor::x() const
+{
+ if (idx >= para->length())
+ return 0;
+ Q3TextStringChar *c = para->at(idx);
+ int curx = c->x;
+ if (!c->rightToLeft &&
+ c->c.isSpace() &&
+ idx > 0 &&
+ para->at(idx - 1)->c != QLatin1Char('\t') &&
+ !c->lineStart &&
+ (para->alignment() & Qt::AlignJustify) == Qt::AlignJustify)
+ curx = para->at(idx - 1)->x + para->string()->width(idx - 1);
+ if (c->rightToLeft)
+ curx += para->string()->width(idx);
+ return curx;
+}
+
+int Q3TextCursor::y() const
+{
+ int dummy, line;
+ para->lineStartOfChar(idx, &dummy, &line);
+ return para->lineY(line);
+}
+
+int Q3TextCursor::globalX() const { return totalOffsetX() + para->rect().x() + x(); }
+int Q3TextCursor::globalY() const { return totalOffsetY() + para->rect().y() + y(); }
+
+Q3TextDocument *Q3TextCursor::document() const
+{
+ return para ? para->document() : 0;
+}
+
+void Q3TextCursor::gotoPosition(Q3TextParagraph* p, int index)
+{
+ if (para && p != para) {
+ while (!indices.isEmpty() && para->document() != p->document())
+ pop();
+ Q_ASSERT(indices.isEmpty() || para->document() == p->document());
+ }
+ para = p;
+ if (index < 0 || index >= para->length()) {
+ qWarning("Q3TextCursor::gotoParagraph Index: %d out of range", index);
+ if (index < 0 || para->length() == 0)
+ index = 0;
+ else
+ index = para->length() - 1;
+ }
+
+ tmpX = -1;
+ idx = index;
+ fixCursorPosition();
+}
+
+bool Q3TextDocument::hasSelection(int id, bool visible) const
+{
+ return (selections.find(id) != selections.end() &&
+ (!visible ||
+ ((Q3TextDocument*)this)->selectionStartCursor(id) !=
+ ((Q3TextDocument*)this)->selectionEndCursor(id)));
+}
+
+void Q3TextDocument::setSelectionStart(int id, const Q3TextCursor &cursor)
+{
+ Q3TextDocumentSelection sel;
+ sel.startCursor = cursor;
+ sel.endCursor = cursor;
+ sel.swapped = false;
+ selections[id] = sel;
+}
+
+Q3TextParagraph *Q3TextDocument::paragAt(int i) const
+{
+ Q3TextParagraph* p = curParag;
+ if (!p || p->paragId() > i)
+ p = fParag;
+ while (p && p->paragId() != i)
+ p = p->next();
+ ((Q3TextDocument*)this)->curParag = p;
+ return p;
+}
+
+
+Q3TextFormat::~Q3TextFormat()
+{
+}
+
+Q3TextFormat::Q3TextFormat()
+ : fm(QFontMetrics(fn)), linkColor(true), logicalFontSize(3), stdSize(qApp->font().pointSize())
+{
+ ref = 0;
+
+ usePixelSizes = false;
+ if (stdSize == -1) {
+ stdSize = qApp->font().pixelSize();
+ usePixelSizes = true;
+ }
+
+ missp = false;
+ ha = AlignNormal;
+ collection = 0;
+}
+
+Q3TextFormat::Q3TextFormat(const Q3StyleSheetItem *style)
+ : fm(QFontMetrics(fn)), linkColor(true), logicalFontSize(3), stdSize(qApp->font().pointSize())
+{
+ ref = 0;
+
+ usePixelSizes = false;
+ if (stdSize == -1) {
+ stdSize = qApp->font().pixelSize();
+ usePixelSizes = true;
+ }
+
+ missp = false;
+ ha = AlignNormal;
+ collection = 0;
+ fn = QFont(style->fontFamily(),
+ style->fontSize(),
+ style->fontWeight(),
+ style->fontItalic());
+ fn.setUnderline(style->fontUnderline());
+ fn.setStrikeOut(style->fontStrikeOut());
+ col = style->color();
+ fm = QFontMetrics(fn);
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ missp = false;
+ ha = AlignNormal;
+ memset(widths, 0, 256);
+ generateKey();
+ addRef();
+}
+
+Q3TextFormat::Q3TextFormat(const QFont &f, const QColor &c, Q3TextFormatCollection *parent)
+ : fn(f), col(c), fm(QFontMetrics(f)), linkColor(true),
+ logicalFontSize(3), stdSize(f.pointSize())
+{
+ ref = 0;
+ usePixelSizes = false;
+ if (stdSize == -1) {
+ stdSize = f.pixelSize();
+ usePixelSizes = true;
+ }
+ collection = parent;
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ missp = false;
+ ha = AlignNormal;
+ memset(widths, 0, 256);
+ generateKey();
+ addRef();
+}
+
+Q3TextFormat::Q3TextFormat(const Q3TextFormat &f)
+ : fm(f.fm)
+{
+ ref = 0;
+ collection = 0;
+ fn = f.fn;
+ col = f.col;
+ leftBearing = f.leftBearing;
+ rightBearing = f.rightBearing;
+ memset(widths, 0, 256);
+ hei = f.hei;
+ asc = f.asc;
+ dsc = f.dsc;
+ stdSize = f.stdSize;
+ usePixelSizes = f.usePixelSizes;
+ logicalFontSize = f.logicalFontSize;
+ missp = f.missp;
+ ha = f.ha;
+ k = f.k;
+ linkColor = f.linkColor;
+ addRef();
+}
+
+Q3TextFormat& Q3TextFormat::operator=(const Q3TextFormat &f)
+{
+ ref = 0;
+ collection = f.collection;
+ fn = f.fn;
+ col = f.col;
+ fm = f.fm;
+ leftBearing = f.leftBearing;
+ rightBearing = f.rightBearing;
+ memset(widths, 0, 256);
+ hei = f.hei;
+ asc = f.asc;
+ dsc = f.dsc;
+ stdSize = f.stdSize;
+ usePixelSizes = f.usePixelSizes;
+ logicalFontSize = f.logicalFontSize;
+ missp = f.missp;
+ ha = f.ha;
+ k = f.k;
+ linkColor = f.linkColor;
+ addRef();
+ return *this;
+}
+
+void Q3TextFormat::update()
+{
+ fm = QFontMetrics(fn);
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ memset(widths, 0, 256);
+ generateKey();
+}
+
+
+QPainter* Q3TextFormat::pntr = 0;
+QFontMetrics* Q3TextFormat::pntr_fm = 0;
+int Q3TextFormat::pntr_ldg=-1;
+int Q3TextFormat::pntr_asc=-1;
+int Q3TextFormat::pntr_hei=-1;
+int Q3TextFormat::pntr_dsc=-1;
+
+void Q3TextFormat::setPainter(QPainter *p)
+{
+ pntr = p;
+}
+
+QPainter* Q3TextFormat::painter()
+{
+ return pntr;
+}
+
+void Q3TextFormat::applyFont(const QFont &f)
+{
+ QFontMetrics fm(pntr->fontMetrics());
+ if (!pntr_fm || pntr->font() != f) {
+ pntr->setFont(f);
+ delete pntr_fm;
+ pntr_fm = new QFontMetrics(pntr->fontMetrics());
+ pntr_ldg = pntr_fm->leading();
+ pntr_asc = pntr_fm->ascent()+(pntr_ldg+1)/2;
+ pntr_hei = pntr_fm->lineSpacing();
+ pntr_dsc = -1;
+ }
+}
+
+int Q3TextFormat::minLeftBearing() const
+{
+ if (!pntr || !pntr->isActive())
+ return leftBearing;
+ applyFont(fn);
+ return pntr_fm->minLeftBearing();
+}
+
+int Q3TextFormat::minRightBearing() const
+{
+ if (!pntr || !pntr->isActive())
+ return rightBearing;
+ applyFont(fn);
+ return pntr_fm->minRightBearing();
+}
+
+int Q3TextFormat::height() const
+{
+ if (!pntr || !pntr->isActive())
+ return hei;
+ applyFont(fn);
+ return pntr_hei;
+}
+
+int Q3TextFormat::ascent() const
+{
+ if (!pntr || !pntr->isActive())
+ return asc;
+ applyFont(fn);
+ return pntr_asc;
+}
+
+int Q3TextFormat::descent() const
+{
+ if (!pntr || !pntr->isActive())
+ return dsc;
+ applyFont(fn);
+ if (pntr_dsc < 0)
+ pntr_dsc = pntr_fm->descent();
+ return pntr_dsc;
+}
+
+int Q3TextFormat::leading() const
+{
+ if (!pntr || !pntr->isActive())
+ return fm.leading();
+ applyFont(fn);
+ return pntr_ldg;
+}
+
+void Q3TextFormat::generateKey()
+{
+ k = getKey(fn, col, isMisspelled(), vAlign());
+}
+
+QString Q3TextFormat::getKey(const QFont &fn, const QColor &col, bool misspelled, VerticalAlignment a)
+{
+ QString k = fn.key();
+ k += QLatin1Char('/');
+ k += QString::number((uint)col.rgb());
+ k += QLatin1Char('/');
+ k += QString::number((int)misspelled);
+ k += QLatin1Char('/');
+ k += QString::number((int)a);
+ return k;
+}
+
+QString Q3TextString::toString(const QVector<Q3TextStringChar> &data)
+{
+ QString s;
+ int l = data.size();
+ s.setUnicode(0, l);
+ const Q3TextStringChar *c = data.data();
+ QChar *uc = (QChar *)s.unicode();
+ while (l--)
+ *(uc++) = (c++)->c;
+
+ return s;
+}
+
+void Q3TextParagraph::setSelection(int id, int start, int end)
+{
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = selections().constFind(id);
+ if (it != mSelections->constEnd()) {
+ if (start == (*it).start && end == (*it).end)
+ return;
+ }
+
+ Q3TextParagraphSelection sel;
+ sel.start = start;
+ sel.end = end;
+ (*mSelections)[id] = sel;
+ setChanged(true, true);
+}
+
+void Q3TextParagraph::removeSelection(int id)
+{
+ if (!hasSelection(id))
+ return;
+ if (mSelections)
+ mSelections->remove(id);
+ setChanged(true, true);
+}
+
+int Q3TextParagraph::selectionStart(int id) const
+{
+ if (!mSelections)
+ return -1;
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(id);
+ if (it == mSelections->constEnd())
+ return -1;
+ return (*it).start;
+}
+
+int Q3TextParagraph::selectionEnd(int id) const
+{
+ if (!mSelections)
+ return -1;
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(id);
+ if (it == mSelections->constEnd())
+ return -1;
+ return (*it).end;
+}
+
+bool Q3TextParagraph::hasSelection(int id) const
+{
+ return mSelections ? mSelections->contains(id) : false;
+}
+
+bool Q3TextParagraph::fullSelected(int id) const
+{
+ if (!mSelections)
+ return false;
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(id);
+ if (it == mSelections->constEnd())
+ return false;
+ return (*it).start == 0 && (*it).end == str->length() - 1;
+}
+
+int Q3TextParagraph::lineY(int l) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineY: line %d out of range!", l);
+ return 0;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ return (*it)->y;
+}
+
+int Q3TextParagraph::lineBaseLine(int l) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineBaseLine: line %d out of range!", l);
+ return 10;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ return (*it)->baseLine;
+}
+
+int Q3TextParagraph::lineHeight(int l) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineHeight: line %d out of range!", l);
+ return 15;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ return (*it)->h;
+}
+
+void Q3TextParagraph::lineInfo(int l, int &y, int &h, int &bl) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineInfo: line %d out of range!", l);
+ qDebug("%d %d", (int)lineStarts.count() - 1, l);
+ y = 0;
+ h = 15;
+ bl = 10;
+ return;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ y = (*it)->y;
+ h = (*it)->h;
+ bl = (*it)->baseLine;
+}
+
+
+void Q3TextParagraph::setAlignment(int a)
+{
+ if (a == (int)align)
+ return;
+ align = a;
+ invalidate(0);
+}
+
+Q3TextFormatter *Q3TextParagraph::formatter() const
+{
+ if (hasdoc)
+ return document()->formatter();
+ if (pseudoDocument()->pFormatter)
+ return pseudoDocument()->pFormatter;
+ return (((Q3TextParagraph*)this)->pseudoDocument()->pFormatter = new Q3TextFormatterBreakWords);
+}
+
+void Q3TextParagraph::setTabArray(int *a)
+{
+ delete [] tArray;
+ tArray = a;
+}
+
+void Q3TextParagraph::setTabStops(int tw)
+{
+ if (hasdoc)
+ document()->setTabStops(tw);
+ else
+ tabStopWidth = tw;
+}
+
+QMap<int, Q3TextParagraphSelection> &Q3TextParagraph::selections() const
+{
+ if (!mSelections)
+ ((Q3TextParagraph *)this)->mSelections = new QMap<int, Q3TextParagraphSelection>;
+ return *mSelections;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+QList<Q3TextCustomItem *> &Q3TextParagraph::floatingItems() const
+{
+ if (!mFloatingItems)
+ ((Q3TextParagraph *)this)->mFloatingItems = new QList<Q3TextCustomItem *>;
+ return *mFloatingItems;
+}
+#endif
+
+Q3TextStringChar::~Q3TextStringChar()
+{
+ if (format())
+ format()->removeRef();
+ if (type) // not Regular
+ delete p.custom;
+}
+
+Q3TextParagraphPseudoDocument::Q3TextParagraphPseudoDocument():pFormatter(0),commandHistory(0), minw(0),wused(0),collection(){}
+Q3TextParagraphPseudoDocument::~Q3TextParagraphPseudoDocument(){ delete pFormatter; delete commandHistory; }
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3richtext_p.h b/src/qt3support/text/q3richtext_p.h
new file mode 100644
index 0000000..e07bb71
--- /dev/null
+++ b/src/qt3support/text/q3richtext_p.h
@@ -0,0 +1,2102 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3RICHTEXT_P_H
+#define Q3RICHTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qapplication.h"
+#include "QtGui/qcolor.h"
+#include "QtCore/qhash.h"
+#include "QtGui/qfont.h"
+#include "QtGui/qfontmetrics.h"
+#include "QtGui/qlayout.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qvector.h"
+#include "QtCore/qstack.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qobject.h"
+#include "QtGui/qpainter.h"
+#include "QtGui/qpixmap.h"
+#include "QtCore/qrect.h"
+#include "QtCore/qsize.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
+#include "Qt3Support/q3stylesheet.h"
+#include "Qt3Support/q3mimefactory.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_RICHTEXT
+
+class Q3TextDocument;
+class Q3TextString;
+class Q3TextPreProcessor;
+class Q3TextFormat;
+class Q3TextCursor;
+class Q3TextParagraph;
+class Q3TextFormatter;
+class Q3TextIndent;
+class Q3TextFormatCollection;
+class Q3StyleSheetItem;
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextCustomItem;
+#endif
+class Q3TextFlow;
+struct QBidiContext;
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextStringChar
+{
+ friend class Q3TextString;
+
+public:
+ // this is never called, initialize variables in Q3TextString::insert()!!!
+ Q3TextStringChar() : nobreak(false), lineStart(0), type(Regular) {p.format=0;}
+ ~Q3TextStringChar();
+
+ struct CustomData
+ {
+ Q3TextFormat *format;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem *custom;
+#endif
+ QString anchorName;
+ QString anchorHref;
+ };
+ enum Type { Regular=0, Custom=1, Anchor=2, CustomAnchor=3 };
+
+ QChar c;
+ // this is the same struct as in qtextengine_p.h. Don't change!
+ uchar softBreak :1; // Potential linebreak point
+ uchar whiteSpace :1; // A unicode whitespace character, except NBSP, ZWNBSP
+ uchar charStop :1; // Valid cursor position (for left/right arrow)
+ uchar nobreak :1;
+
+ uchar lineStart : 1;
+ uchar /*Type*/ type : 2;
+ uchar bidiLevel :7;
+ uchar rightToLeft : 1;
+
+ int x;
+ union {
+ Q3TextFormat* format;
+ CustomData* custom;
+ } p;
+
+
+ int height() const;
+ int ascent() const;
+ int descent() const;
+ bool isCustom() const { return (type & Custom) != 0; }
+ Q3TextFormat *format() const;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem *customItem() const;
+#endif
+ void setFormat(Q3TextFormat *f);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void setCustomItem(Q3TextCustomItem *i);
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void loseCustomItem();
+#endif
+
+
+ bool isAnchor() const { return (type & Anchor) != 0; }
+ bool isLink() const { return isAnchor() && p.custom->anchorHref.count(); }
+ QString anchorName() const;
+ QString anchorHref() const;
+ void setAnchor(const QString& name, const QString& href);
+
+ Q3TextStringChar(const Q3TextStringChar &) {
+ Q_ASSERT(false);
+ }
+private:
+ Q3TextStringChar &operator=(const Q3TextStringChar &) {
+ //abort();
+ return *this;
+ }
+ friend class Q3TextParagraph;
+};
+
+Q_DECLARE_TYPEINFO(Q3TextStringChar, Q_PRIMITIVE_TYPE);
+
+class Q_COMPAT_EXPORT Q3TextString
+{
+public:
+
+ Q3TextString();
+ Q3TextString(const Q3TextString &s);
+ virtual ~Q3TextString();
+
+ static QString toString(const QVector<Q3TextStringChar> &data);
+ QString toString() const;
+
+ inline Q3TextStringChar &at(int i) const {
+ return const_cast<Q3TextString *>(this)->data[i];
+ }
+ inline int length() const { return data.size(); }
+
+ int width(int idx) const;
+
+ void insert(int index, const QString &s, Q3TextFormat *f);
+ void insert(int index, const QChar *unicode, int len, Q3TextFormat *f);
+ void insert(int index, Q3TextStringChar *c, bool doAddRefFormat = false);
+ void truncate(int index);
+ void remove(int index, int len);
+ void clear();
+
+ void setFormat(int index, Q3TextFormat *f, bool useCollection);
+
+ void setBidi(bool b) { bidi = b; }
+ bool isBidi() const;
+ bool isRightToLeft() const;
+ QChar::Direction direction() const;
+ void setDirection(QChar::Direction dr) { dir = dr; bidiDirty = true; }
+
+ QVector<Q3TextStringChar> rawData() const { return data; }
+
+ void operator=(const QString &s) { clear(); insert(0, s, 0); }
+ void operator+=(const QString &s) { insert(length(), s, 0); }
+ void prepend(const QString &s) { insert(0, s, 0); }
+ int appendParagraphs( Q3TextParagraph *start, Q3TextParagraph *end );
+
+ // return next and previous valid cursor positions.
+ bool validCursorPosition(int idx);
+ int nextCursorPosition(int idx);
+ int previousCursorPosition(int idx);
+
+private:
+ void checkBidi() const;
+
+ QVector<Q3TextStringChar> data;
+ QString stringCache;
+ uint bidiDirty : 1;
+ uint bidi : 1; // true when the paragraph has right to left characters
+ uint rightToLeft : 1;
+ uint dir : 5;
+};
+
+inline bool Q3TextString::isBidi() const
+{
+ if (bidiDirty)
+ checkBidi();
+ return bidi;
+}
+
+inline bool Q3TextString::isRightToLeft() const
+{
+ if (bidiDirty)
+ checkBidi();
+ return rightToLeft;
+}
+
+inline QString Q3TextString::toString() const
+{
+ if (bidiDirty)
+ checkBidi();
+ return stringCache;
+}
+
+inline QChar::Direction Q3TextString::direction() const
+{
+ return rightToLeft ? QChar::DirR : QChar::DirL;
+}
+
+inline int Q3TextString::nextCursorPosition(int next)
+{
+ if (bidiDirty)
+ checkBidi();
+
+ const Q3TextStringChar *c = data.data();
+ int len = length();
+
+ if (next < len - 1) {
+ next++;
+ while (next < len - 1 && !c[next].charStop)
+ next++;
+ }
+ return next;
+}
+
+inline int Q3TextString::previousCursorPosition(int prev)
+{
+ if (bidiDirty)
+ checkBidi();
+
+ const Q3TextStringChar *c = data.data();
+
+ if (prev) {
+ prev--;
+ while (prev && !c[prev].charStop)
+ prev--;
+ }
+ return prev;
+}
+
+inline bool Q3TextString::validCursorPosition(int idx)
+{
+ if (bidiDirty)
+ checkBidi();
+
+ return (at(idx).charStop);
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextCursor
+{
+public:
+ Q3TextCursor(Q3TextDocument * = 0);
+ Q3TextCursor(const Q3TextCursor &c);
+ Q3TextCursor &operator=(const Q3TextCursor &c);
+ virtual ~Q3TextCursor();
+
+ bool operator==(const Q3TextCursor &c) const;
+ bool operator!=(const Q3TextCursor &c) const { return !(*this == c); }
+
+ inline Q3TextParagraph *paragraph() const { return para; }
+
+ Q3TextDocument *document() const;
+ int index() const;
+
+ void gotoPosition(Q3TextParagraph* p, int index = 0);
+ void setIndex(int index) { gotoPosition(paragraph(), index); }
+ void setParagraph(Q3TextParagraph*p) { gotoPosition(p, 0); }
+
+ void gotoLeft();
+ void gotoRight();
+ void gotoNextLetter();
+ void gotoPreviousLetter();
+ void gotoUp();
+ void gotoDown();
+ void gotoLineEnd();
+ void gotoLineStart();
+ void gotoHome();
+ void gotoEnd();
+ void gotoPageUp(int visibleHeight);
+ void gotoPageDown(int visibleHeight);
+ void gotoNextWord(bool onlySpace = false);
+ void gotoPreviousWord(bool onlySpace = false);
+ void gotoWordLeft();
+ void gotoWordRight();
+
+ void insert(const QString &s, bool checkNewLine, QVector<Q3TextStringChar> *formatting = 0);
+ void splitAndInsertEmptyParagraph(bool ind = true, bool updateIds = true);
+ bool remove();
+ bool removePreviousChar();
+ void indent();
+
+ bool atParagStart();
+ bool atParagEnd();
+
+ int x() const; // x in current paragraph
+ int y() const; // y in current paragraph
+
+ int globalX() const;
+ int globalY() const;
+
+ Q3TextParagraph *topParagraph() const { return paras.isEmpty() ? para : paras.first(); }
+ int offsetX() const { return ox; } // inner document offset
+ int offsetY() const { return oy; } // inner document offset
+ int totalOffsetX() const; // total document offset
+ int totalOffsetY() const; // total document offset
+
+ bool place(const QPoint &pos, Q3TextParagraph *s) { return place(pos, s, false); }
+ bool place(const QPoint &pos, Q3TextParagraph *s, bool link);
+ void restoreState();
+
+
+ int nestedDepth() const { return (int)indices.count(); } //### size_t/int cast
+ void oneUp() { if (!indices.isEmpty()) pop(); }
+ void setValid(bool b) { valid = b; }
+ bool isValid() const { return valid; }
+
+ void fixCursorPosition();
+private:
+ enum Operation { EnterBegin, EnterEnd, Next, Prev, Up, Down };
+
+ void push();
+ void pop();
+ bool processNesting(Operation op);
+ void invalidateNested();
+ void gotoIntoNested(const QPoint &globalPos);
+
+ Q3TextParagraph *para;
+ int idx, tmpX;
+ int ox, oy;
+ QStack<int> indices;
+ QStack<Q3TextParagraph*> paras;
+ QStack<int> xOffsets;
+ QStack<int> yOffsets;
+ uint valid : 1;
+
+};
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextCommand
+{
+public:
+ enum Commands { Invalid, Insert, Delete, Format, Style };
+
+ Q3TextCommand(Q3TextDocument *dc) : doc(dc), cursor(dc) {}
+ virtual ~Q3TextCommand();
+
+ virtual Commands type() const;
+
+ virtual Q3TextCursor *execute(Q3TextCursor *c) = 0;
+ virtual Q3TextCursor *unexecute(Q3TextCursor *c) = 0;
+
+protected:
+ Q3TextDocument *doc;
+ Q3TextCursor cursor;
+
+};
+
+class Q_COMPAT_EXPORT Q3TextCommandHistory
+{
+public:
+ Q3TextCommandHistory(int s) : current(-1), steps(s) { }
+ virtual ~Q3TextCommandHistory(); // ### why is it virtual?
+
+ void clear();
+
+ void addCommand(Q3TextCommand *cmd);
+ Q3TextCursor *undo(Q3TextCursor *c);
+ Q3TextCursor *redo(Q3TextCursor *c);
+
+ bool isUndoAvailable();
+ bool isRedoAvailable();
+
+ void setUndoDepth(int depth) { steps = depth; }
+ int undoDepth() const { return steps; }
+
+ int historySize() const { return history.count(); }
+ int currentPosition() const { return current; }
+
+private:
+ QList<Q3TextCommand *> history;
+ int current, steps;
+};
+
+inline Q3TextCommandHistory::~Q3TextCommandHistory()
+{
+ clear();
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextCustomItem
+{
+public:
+ Q3TextCustomItem(Q3TextDocument *p)
+ : xpos(0), ypos(-1), width(-1), height(0), parent(p)
+ {}
+ virtual ~Q3TextCustomItem();
+ virtual void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected) = 0;
+
+ virtual void adjustToPainter(QPainter*);
+
+ enum Placement { PlaceInline = 0, PlaceLeft, PlaceRight };
+ virtual Placement placement() const;
+ bool placeInline() { return placement() == PlaceInline; }
+
+ virtual bool ownLine() const;
+ virtual void resize(int nwidth);
+ virtual void invalidate();
+ virtual int ascent() const { return height; }
+
+ virtual bool isNested() const;
+ virtual int minimumWidth() const;
+
+ virtual QString richText() const;
+
+ int xpos; // used for floating items
+ int ypos; // used for floating items
+ int width;
+ int height;
+
+ QRect geometry() const { return QRect(xpos, ypos, width, height); }
+
+ virtual bool enter(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd = false);
+ virtual bool enterAt(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &);
+ virtual bool next(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool prev(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool down(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool up(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+
+ void setParagraph(Q3TextParagraph *p) { parag = p; }
+ Q3TextParagraph *paragraph() const { return parag; }
+
+ Q3TextDocument *parent;
+ Q3TextParagraph *parag;
+
+ virtual void pageBreak(int y, Q3TextFlow* flow);
+};
+#endif
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextImage : public Q3TextCustomItem
+{
+public:
+ Q3TextImage(Q3TextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ Q3MimeSourceFactory &factory);
+ virtual ~Q3TextImage();
+
+ Placement placement() const { return place; }
+ void adjustToPainter(QPainter*);
+ int minimumWidth() const { return width; }
+
+ QString richText() const;
+
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected);
+
+private:
+ QRegion* reg;
+ QPixmap pm;
+ Placement place;
+ int tmpwidth, tmpheight;
+ QMap<QString, QString> attributes;
+ QString imgId;
+
+};
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextHorizontalLine : public Q3TextCustomItem
+{
+public:
+ Q3TextHorizontalLine(Q3TextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ Q3MimeSourceFactory &factory);
+ virtual ~Q3TextHorizontalLine();
+
+ void adjustToPainter(QPainter*);
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected);
+ QString richText() const;
+
+ bool ownLine() const { return true; }
+
+private:
+ int tmpheight;
+ QColor color;
+ bool shade;
+
+};
+#endif
+
+class Q_COMPAT_EXPORT Q3TextFlow
+{
+ friend class Q3TextDocument;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ friend class Q3TextTableCell;
+#endif
+
+public:
+ Q3TextFlow();
+ virtual ~Q3TextFlow();
+
+ virtual void setWidth(int width);
+ int width() const;
+
+ virtual void setPageSize(int ps);
+ int pageSize() const { return pagesize; }
+
+ virtual int adjustLMargin(int yp, int h, int margin, int space);
+ virtual int adjustRMargin(int yp, int h, int margin, int space);
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ virtual void registerFloatingItem(Q3TextCustomItem* item);
+ virtual void unregisterFloatingItem(Q3TextCustomItem* item);
+#endif
+ virtual QRect boundingRect() const;
+ virtual void drawFloatingItems(QPainter* p, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected);
+
+ virtual int adjustFlow(int y, int w, int h); // adjusts y according to the defined pagesize. Returns the shift.
+
+ virtual bool isEmpty();
+
+ void clear();
+
+private:
+ int w;
+ int pagesize;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ QList<Q3TextCustomItem *> leftItems;
+ QList<Q3TextCustomItem *> rightItems;
+#endif
+};
+
+inline int Q3TextFlow::width() const { return w; }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextTable;
+
+class Q_COMPAT_EXPORT Q3TextTableCell : public QLayoutItem
+{
+ friend class Q3TextTable;
+
+public:
+ Q3TextTableCell(Q3TextTable* table,
+ int row, int column,
+ const QMap<QString, QString> &attr,
+ const Q3StyleSheetItem* style,
+ const Q3TextFormat& fmt, const QString& context,
+ Q3MimeSourceFactory &factory, Q3StyleSheet *sheet, const QString& doc);
+ virtual ~Q3TextTableCell();
+
+ QSize sizeHint() const ;
+ QSize minimumSize() const ;
+ QSize maximumSize() const ;
+ Qt::Orientations expandingDirections() const;
+ bool isEmpty() const;
+ void setGeometry(const QRect&) ;
+ QRect geometry() const;
+
+ bool hasHeightForWidth() const;
+ int heightForWidth(int) const;
+
+ void adjustToPainter(QPainter*);
+
+ int row() const { return row_; }
+ int column() const { return col_; }
+ int rowspan() const { return rowspan_; }
+ int colspan() const { return colspan_; }
+ int stretch() const { return stretch_; }
+
+ Q3TextDocument* richText() const { return richtext; }
+ Q3TextTable* table() const { return parent; }
+
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &cg, bool selected);
+
+ QBrush *backGround() const { return background; }
+ virtual void invalidate();
+
+ int verticalAlignmentOffset() const;
+ int horizontalAlignmentOffset() const;
+
+private:
+ QRect geom;
+ Q3TextTable* parent;
+ Q3TextDocument* richtext;
+ int row_;
+ int col_;
+ int rowspan_;
+ int colspan_;
+ int stretch_;
+ int maxw;
+ int minw;
+ bool hasFixedWidth;
+ QBrush *background;
+ int cached_width;
+ int cached_sizehint;
+ QMap<QString, QString> attributes;
+ int align;
+};
+#endif
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextTable: public Q3TextCustomItem
+{
+ friend class Q3TextTableCell;
+
+public:
+ Q3TextTable(Q3TextDocument *p, const QMap<QString, QString> &attr);
+ virtual ~Q3TextTable();
+
+ void adjustToPainter(QPainter *p);
+ void pageBreak(int y, Q3TextFlow* flow);
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected);
+
+ bool noErase() const { return true; }
+ bool ownLine() const { return true; }
+ Placement placement() const { return place; }
+ bool isNested() const { return true; }
+ void resize(int nwidth);
+ virtual void invalidate();
+
+ virtual bool enter(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd = false);
+ virtual bool enterAt(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &pos);
+ virtual bool next(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool prev(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool down(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool up(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+
+ QString richText() const;
+
+ int minimumWidth() const;
+
+ QList<Q3TextTableCell *> tableCells() const { return cells; }
+
+ bool isStretching() const { return stretch; }
+
+private:
+ void format(int w);
+ void addCell(Q3TextTableCell* cell);
+
+private:
+ QGridLayout* layout;
+ QList<Q3TextTableCell *> cells;
+ int cachewidth;
+ int fixwidth;
+ int cellpadding;
+ int cellspacing;
+ int border;
+ int outerborder;
+ int stretch;
+ int innerborder;
+ int us_cp, us_ib, us_b, us_ob, us_cs;
+ int us_fixwidth;
+ QMap<QString, QString> attributes;
+ QMap<Q3TextCursor*, int> currCell;
+ Placement place;
+ void adjustCells(int y , int shift);
+ int pageBreakFor;
+};
+#endif
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextTableCell;
+class Q3TextParagraph;
+#endif
+
+struct Q_COMPAT_EXPORT Q3TextDocumentSelection
+{
+ Q3TextCursor startCursor, endCursor;
+ bool swapped;
+ Q_DUMMY_COMPARISON_OPERATOR(Q3TextDocumentSelection)
+};
+
+class Q_COMPAT_EXPORT Q3TextDocument : public QObject
+{
+ Q_OBJECT
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ friend class Q3TextTableCell;
+#endif
+ friend class Q3TextCursor;
+ friend class Q3TextEdit;
+ friend class Q3TextParagraph;
+
+public:
+ enum SelectionIds {
+ Standard = 0,
+ Temp = 32000 // This selection must not be drawn, it's used e.g. by undo/redo to
+ // remove multiple lines with removeSelectedText()
+ };
+
+ Q3TextDocument(Q3TextDocument *p);
+ virtual ~Q3TextDocument();
+
+ Q3TextDocument *parent() const { return par; }
+ Q3TextParagraph *parentParagraph() const { return parentPar; }
+
+ void setText(const QString &text, const QString &context);
+ QMap<QString, QString> attributes() const { return attribs; }
+ void setAttributes(const QMap<QString, QString> &attr) { attribs = attr; }
+
+ QString text() const;
+ QString text(int parag) const;
+ QString originalText() const;
+
+ int x() const;
+ int y() const;
+ int width() const;
+ int widthUsed() const;
+ int visibleWidth() const;
+ int height() const;
+ void setWidth(int w);
+ int minimumWidth() const;
+ bool setMinimumWidth(int needed, int used = -1, Q3TextParagraph *parag = 0);
+
+ void setY(int y);
+ int leftMargin() const;
+ void setLeftMargin(int lm);
+ int rightMargin() const;
+ void setRightMargin(int rm);
+
+ Q3TextParagraph *firstParagraph() const;
+ Q3TextParagraph *lastParagraph() const;
+ void setFirstParagraph(Q3TextParagraph *p);
+ void setLastParagraph(Q3TextParagraph *p);
+
+ void invalidate();
+
+ void setPreProcessor(Q3TextPreProcessor *sh);
+ Q3TextPreProcessor *preProcessor() const;
+
+ void setFormatter(Q3TextFormatter *f);
+ Q3TextFormatter *formatter() const;
+
+ void setIndent(Q3TextIndent *i);
+ Q3TextIndent *indent() const;
+
+ QColor selectionColor(int id) const;
+ QColor selectionTextColor(int id) const;
+ bool hasSelectionTextColor(int id) const;
+ void setSelectionColor(int id, const QColor &c);
+ void setSelectionTextColor(int id, const QColor &b);
+ bool hasSelection(int id, bool visible = false) const;
+ void setSelectionStart(int id, const Q3TextCursor &cursor);
+ bool setSelectionEnd(int id, const Q3TextCursor &cursor);
+ void selectAll(int id);
+ bool removeSelection(int id);
+ void selectionStart(int id, int &paragId, int &index);
+ Q3TextCursor selectionStartCursor(int id);
+ Q3TextCursor selectionEndCursor(int id);
+ void selectionEnd(int id, int &paragId, int &index);
+ void setFormat(int id, Q3TextFormat *f, int flags);
+ int numSelections() const { return nSelections; }
+ void addSelection(int id);
+
+ QString selectedText(int id, bool asRichText = false) const;
+ void removeSelectedText(int id, Q3TextCursor *cursor);
+ void indentSelection(int id);
+
+ Q3TextParagraph *paragAt(int i) const;
+
+ void addCommand(Q3TextCommand *cmd);
+ Q3TextCursor *undo(Q3TextCursor *c = 0);
+ Q3TextCursor *redo(Q3TextCursor *c = 0);
+ Q3TextCommandHistory *commands() const { return commandHistory; }
+
+ Q3TextFormatCollection *formatCollection() const;
+
+ bool find(Q3TextCursor &cursor, const QString &expr, bool cs, bool wo, bool forward);
+
+ void setTextFormat(Qt::TextFormat f);
+ Qt::TextFormat textFormat() const;
+
+ bool inSelection(int selId, const QPoint &pos) const;
+
+ Q3StyleSheet *styleSheet() const { return sheet_; }
+#ifndef QT_NO_MIME
+ Q3MimeSourceFactory *mimeSourceFactory() const { return factory_; }
+#endif
+ QString context() const { return contxt; }
+
+ void setStyleSheet(Q3StyleSheet *s);
+ void setDefaultFormat(const QFont &font, const QColor &color);
+#ifndef QT_NO_MIME
+ void setMimeSourceFactory(Q3MimeSourceFactory *f) { if (f) factory_ = f; }
+#endif
+ void setContext(const QString &c) { if (!c.isEmpty()) contxt = c; }
+
+ void setUnderlineLinks(bool b);
+ bool underlineLinks() const { return underlLinks; }
+
+ void setPaper(QBrush *brush) { if (backBrush) delete backBrush; backBrush = brush; }
+ QBrush *paper() const { return backBrush; }
+
+ void doLayout(QPainter *p, int w);
+ void draw(QPainter *p, const QRect& rect, const QPalette &pal, const QBrush *paper = 0);
+
+ void drawParagraph(QPainter *p, Q3TextParagraph *parag, int cx, int cy, int cw, int ch,
+ QPixmap *&doubleBuffer, const QPalette &pal,
+ bool drawCursor, Q3TextCursor *cursor, bool resetChanged = true);
+ Q3TextParagraph *draw(QPainter *p, int cx, int cy, int cw, int ch, const QPalette &pal,
+ bool onlyChanged = false, bool drawCursor = false, Q3TextCursor *cursor = 0,
+ bool resetChanged = true);
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ static Q3TextCustomItem* tag(Q3StyleSheet *sheet, const QString& name,
+ const QMap<QString, QString> &attr,
+ const QString& context,
+ const Q3MimeSourceFactory& factory,
+ bool emptyTag, Q3TextDocument *doc);
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void registerCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p);
+ void unregisterCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p);
+#endif
+
+ void setFlow(Q3TextFlow *f);
+ void takeFlow();
+ Q3TextFlow *flow() const { return flow_; }
+ bool isPageBreakEnabled() const { return pages; }
+ void setPageBreakEnabled(bool b) { pages = b; }
+
+ void setUseFormatCollection(bool b) { useFC = b; }
+ bool useFormatCollection() const { return useFC; }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextTableCell *tableCell() const { return tc; }
+ void setTableCell(Q3TextTableCell *c) { tc = c; }
+#endif
+
+ void setPlainText(const QString &text);
+ void setRichText(const QString &text, const QString &context, const Q3TextFormat *initialFormat = 0);
+ QString richText() const;
+ QString plainText() const;
+
+ bool focusNextPrevChild(bool next);
+
+ int alignment() const;
+ void setAlignment(int a);
+
+ int *tabArray() const;
+ int tabStopWidth() const;
+ void setTabArray(int *a);
+ void setTabStops(int tw);
+
+ void setUndoDepth(int depth) { commandHistory->setUndoDepth(depth); }
+ int undoDepth() const { return commandHistory->undoDepth(); }
+
+ int length() const;
+ void clear(bool createEmptyParag = false);
+
+ virtual Q3TextParagraph *createParagraph(Q3TextDocument *, Q3TextParagraph *pr = 0, Q3TextParagraph *nx = 0, bool updateIds = true);
+ void insertChild(Q3TextDocument *dc) { childList.append(dc); }
+ void removeChild(Q3TextDocument *dc) { childList.removeAll(dc); }
+ QList<Q3TextDocument *> children() const { return childList; }
+
+ bool hasFocusParagraph() const;
+ QString focusHref() const;
+ QString focusName() const;
+
+ void invalidateOriginalText() { oTextValid = false; oText = QLatin1String(""); }
+
+Q_SIGNALS:
+ void minimumWidthChanged(int);
+
+private:
+ Q_DISABLE_COPY(Q3TextDocument)
+
+ void init();
+ QPixmap *bufferPixmap(const QSize &s);
+ // HTML parser
+ bool hasPrefix(const QChar* doc, int length, int pos, QChar c);
+ bool hasPrefix(const QChar* doc, int length, int pos, const QString& s);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem* parseTable(const QMap<QString, QString> &attr, const Q3TextFormat &fmt,
+ const QChar* doc, int length, int& pos, Q3TextParagraph *curpar);
+#endif
+ bool eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp = false);
+ bool eat(const QChar* doc, int length, int& pos, QChar c);
+ QString parseOpenTag(const QChar* doc, int length, int& pos, QMap<QString, QString> &attr, bool& emptyTag);
+ QString parseCloseTag(const QChar* doc, int length, int& pos);
+ QChar parseHTMLSpecialChar(const QChar* doc, int length, int& pos);
+ QString parseWord(const QChar* doc, int length, int& pos, bool lower = true);
+ QChar parseChar(const QChar* doc, int length, int& pos, Q3StyleSheetItem::WhiteSpaceMode wsm);
+ void setRichTextInternal(const QString &text, Q3TextCursor* cursor = 0, const Q3TextFormat *initialFormat = 0);
+ void setRichTextMarginsInternal(QList< QVector<Q3StyleSheetItem *> *>& styles, Q3TextParagraph* stylesPar);
+
+ struct Q_COMPAT_EXPORT Focus {
+ Q3TextParagraph *parag;
+ int start, len;
+ QString href;
+ QString name;
+ };
+
+ int cx, cy, cw, vw;
+ Q3TextParagraph *fParag, *lParag;
+ Q3TextPreProcessor *pProcessor;
+ struct SelectionColor {
+ QColor background;
+ QColor text;
+ };
+ QMap<int, SelectionColor> selectionColors;
+ QMap<int, Q3TextDocumentSelection> selections;
+ Q3TextCommandHistory *commandHistory;
+ Q3TextFormatter *pFormatter;
+ Q3TextIndent *indenter;
+ Q3TextFormatCollection *fCollection;
+ Qt::TextFormat txtFormat;
+ uint preferRichText : 1;
+ uint pages : 1;
+ uint useFC : 1;
+ uint withoutDoubleBuffer : 1;
+ uint underlLinks : 1;
+ uint nextDoubleBuffered : 1;
+ uint oTextValid : 1;
+ uint mightHaveCustomItems : 1;
+ int align;
+ int nSelections;
+ Q3TextFlow *flow_;
+ Q3TextDocument *par;
+ Q3TextParagraph *parentPar;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextTableCell *tc;
+#endif
+ QBrush *backBrush;
+ QPixmap *buf_pixmap;
+ Focus focusIndicator;
+ int minw;
+ int wused;
+ int leftmargin;
+ int rightmargin;
+ Q3TextParagraph *minwParag, *curParag;
+ Q3StyleSheet* sheet_;
+#ifndef QT_NO_MIME
+ Q3MimeSourceFactory* factory_;
+#endif
+ QString contxt;
+ QMap<QString, QString> attribs;
+ int *tArray;
+ int tStopWidth;
+ int uDepth;
+ QString oText;
+ QList<Q3TextDocument *> childList;
+ QColor linkColor, bodyText;
+ double scaleFontsFactor;
+
+ short list_tm,list_bm, list_lm, li_tm, li_bm, par_tm, par_bm;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+class Q_COMPAT_EXPORT Q3TextDeleteCommand : public Q3TextCommand
+{
+public:
+ Q3TextDeleteCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
+ const QByteArray& oldStyle);
+ Q3TextDeleteCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str);
+ virtual ~Q3TextDeleteCommand();
+
+ Commands type() const { return Delete; }
+ Q3TextCursor *execute(Q3TextCursor *c);
+ Q3TextCursor *unexecute(Q3TextCursor *c);
+
+protected:
+ int id, index;
+ Q3TextParagraph *parag;
+ QVector<Q3TextStringChar> text;
+ QByteArray styleInformation;
+
+};
+
+class Q_COMPAT_EXPORT Q3TextInsertCommand : public Q3TextDeleteCommand
+{
+public:
+ Q3TextInsertCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
+ const QByteArray& oldStyleInfo)
+ : Q3TextDeleteCommand(dc, i, idx, str, oldStyleInfo) {}
+ Q3TextInsertCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str)
+ : Q3TextDeleteCommand(p, idx, str) {}
+ virtual ~Q3TextInsertCommand() {}
+
+ Commands type() const { return Insert; }
+ Q3TextCursor *execute(Q3TextCursor *c) { return Q3TextDeleteCommand::unexecute(c); }
+ Q3TextCursor *unexecute(Q3TextCursor *c) { return Q3TextDeleteCommand::execute(c); }
+
+};
+
+class Q_COMPAT_EXPORT Q3TextFormatCommand : public Q3TextCommand
+{
+public:
+ Q3TextFormatCommand(Q3TextDocument *dc, int sid, int sidx, int eid, int eidx, const QVector<Q3TextStringChar> &old, Q3TextFormat *f, int fl);
+ virtual ~Q3TextFormatCommand();
+
+ Commands type() const { return Format; }
+ Q3TextCursor *execute(Q3TextCursor *c);
+ Q3TextCursor *unexecute(Q3TextCursor *c);
+
+protected:
+ int startId, startIndex, endId, endIndex;
+ Q3TextFormat *format;
+ QVector<Q3TextStringChar> oldFormats;
+ int flags;
+
+};
+
+class Q_COMPAT_EXPORT Q3TextStyleCommand : public Q3TextCommand
+{
+public:
+ Q3TextStyleCommand(Q3TextDocument *dc, int fParag, int lParag, const QByteArray& beforeChange );
+ virtual ~Q3TextStyleCommand() {}
+
+ Commands type() const { return Style; }
+ Q3TextCursor *execute(Q3TextCursor *c);
+ Q3TextCursor *unexecute(Q3TextCursor *c);
+
+ static QByteArray readStyleInformation( Q3TextDocument* dc, int fParag, int lParag);
+ static void writeStyleInformation( Q3TextDocument* dc, int fParag, const QByteArray& style);
+
+private:
+ int firstParag, lastParag;
+ QByteArray before;
+ QByteArray after;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+struct Q_COMPAT_EXPORT Q3TextParagraphSelection
+{
+ int start, end;
+ Q_DUMMY_COMPARISON_OPERATOR(Q3TextParagraphSelection)
+};
+
+struct Q_COMPAT_EXPORT QTextLineStart
+{
+ QTextLineStart() : y(0), baseLine(0), h(0)
+ { }
+ QTextLineStart(int y_, int bl, int h_) : y(y_), baseLine(bl), h(h_),
+ w(0)
+ { }
+
+public:
+ int y, baseLine, h;
+ int w;
+};
+
+class Q_COMPAT_EXPORT Q3TextParagraphData
+{
+public:
+ Q3TextParagraphData() {}
+ virtual ~Q3TextParagraphData();
+ virtual void join(Q3TextParagraphData *);
+};
+
+class Q3TextParagraphPseudoDocument;
+
+class Q3SyntaxHighlighter;
+
+class Q_COMPAT_EXPORT Q3TextParagraph
+{
+ friend class Q3TextDocument;
+ friend class Q3TextCursor;
+ friend class Q3SyntaxHighlighter;
+
+public:
+ Q3TextParagraph(Q3TextDocument *dc, Q3TextParagraph *pr = 0, Q3TextParagraph *nx = 0, bool updateIds = true);
+ ~Q3TextParagraph();
+
+ Q3TextString *string() const;
+ Q3TextStringChar *at(int i) const; // maybe remove later
+ int leftGap() const;
+ int length() const; // maybe remove later
+
+ void setListStyle(Q3StyleSheetItem::ListStyle ls) { lstyle = ls; changed = true; }
+ Q3StyleSheetItem::ListStyle listStyle() const { return (Q3StyleSheetItem::ListStyle)lstyle; }
+ void setListItem(bool li);
+ bool isListItem() const { return litem; }
+ void setListValue(int v) { list_val = v; }
+ int listValue() const { return list_val > 0 ? list_val : -1; }
+
+ void setListDepth(int depth);
+ int listDepth() const { return ldepth; }
+
+// void setFormat(Q3TextFormat *fm);
+// Q3TextFormat *paragFormat() const;
+
+ inline Q3TextDocument *document() const {
+ if (hasdoc) return (Q3TextDocument*) docOrPseudo;
+ return 0;
+ }
+ Q3TextParagraphPseudoDocument *pseudoDocument() const;
+
+ QRect rect() const;
+ void setHeight(int h) { r.setHeight(h); }
+ void show();
+ void hide();
+ bool isVisible() const { return visible; }
+
+ Q3TextParagraph *prev() const;
+ Q3TextParagraph *next() const;
+ void setPrev(Q3TextParagraph *s);
+ void setNext(Q3TextParagraph *s);
+
+ void insert(int index, const QString &s);
+ void insert(int index, const QChar *unicode, int len);
+ void append(const QString &s, bool reallyAtEnd = false);
+ void truncate(int index);
+ void remove(int index, int len);
+ void join(Q3TextParagraph *s);
+
+ void invalidate(int chr);
+
+ void move(int &dy);
+ void format(int start = -1, bool doMove = true);
+
+ bool isValid() const;
+ bool hasChanged() const;
+ void setChanged(bool b, bool recursive = false);
+
+ int lineHeightOfChar(int i, int *bl = 0, int *y = 0) const;
+ Q3TextStringChar *lineStartOfChar(int i, int *index = 0, int *line = 0) const;
+ int lines() const;
+ Q3TextStringChar *lineStartOfLine(int line, int *index = 0) const;
+ int lineY(int l) const;
+ int lineBaseLine(int l) const;
+ int lineHeight(int l) const;
+ void lineInfo(int l, int &y, int &h, int &bl) const;
+
+ void setSelection(int id, int start, int end);
+ void removeSelection(int id);
+ int selectionStart(int id) const;
+ int selectionEnd(int id) const;
+ bool hasSelection(int id) const;
+ bool hasAnySelection() const;
+ bool fullSelected(int id) const;
+
+ void setEndState(int s);
+ int endState() const;
+
+ void setParagId(int i);
+ int paragId() const;
+
+ bool firstPreProcess() const;
+ void setFirstPreProcess(bool b);
+
+ void indent(int *oldIndent = 0, int *newIndent = 0);
+
+ void setExtraData(Q3TextParagraphData *data);
+ Q3TextParagraphData *extraData() const;
+
+ QMap<int, QTextLineStart*> &lineStartList();
+
+ void setFormat(int index, int len, Q3TextFormat *f, bool useCollection = true, int flags = -1);
+
+ void setAlignment(int a);
+ int alignment() const;
+
+ void paint(QPainter &painter, const QPalette &pal, Q3TextCursor *cursor = 0,
+ bool drawSelections = false, int clipx = -1, int clipy = -1,
+ int clipw = -1, int cliph = -1);
+
+ int topMargin() const;
+ int bottomMargin() const;
+ int leftMargin() const;
+ int firstLineMargin() const;
+ int rightMargin() const;
+ int lineSpacing() const;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void registerFloatingItem(Q3TextCustomItem *i);
+ void unregisterFloatingItem(Q3TextCustomItem *i);
+#endif
+
+ void setFullWidth(bool b) { fullWidth = b; }
+ bool isFullWidth() const { return fullWidth; }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextTableCell *tableCell() const;
+#endif
+
+ QBrush *background() const;
+
+ int documentWidth() const;
+ int documentVisibleWidth() const;
+ int documentX() const;
+ int documentY() const;
+ Q3TextFormatCollection *formatCollection() const;
+ Q3TextFormatter *formatter() const;
+
+ int nextTab(int i, int x);
+ int *tabArray() const;
+ void setTabArray(int *a);
+ void setTabStops(int tw);
+
+ void adjustToPainter(QPainter *p);
+
+ void setNewLinesAllowed(bool b);
+ bool isNewLinesAllowed() const;
+
+ QString richText() const;
+
+ void addCommand(Q3TextCommand *cmd);
+ Q3TextCursor *undo(Q3TextCursor *c = 0);
+ Q3TextCursor *redo(Q3TextCursor *c = 0);
+ Q3TextCommandHistory *commands() const;
+ void copyParagData(Q3TextParagraph *parag);
+
+ void setBreakable(bool b) { breakable = b; }
+ bool isBreakable() const { return breakable; }
+
+ void setBackgroundColor(const QColor &c);
+ QColor *backgroundColor() const { return bgcol; }
+ void clearBackgroundColor();
+
+ void setMovedDown(bool b) { movedDown = b; }
+ bool wasMovedDown() const { return movedDown; }
+
+ void setDirection(QChar::Direction);
+ QChar::Direction direction() const;
+ void setPaintDevice(QPaintDevice *pd) { paintdevice = pd; }
+
+ void readStyleInformation(QDataStream& stream);
+ void writeStyleInformation(QDataStream& stream) const;
+
+protected:
+ void setColorForSelection(QColor &c, QPainter &p, const QPalette &pal, int selection);
+ void drawLabel(QPainter* p, int x, int y, int w, int h, int base, const QPalette &pal);
+ void drawString(QPainter &painter, const QString &str, int start, int len, int xstart,
+ int y, int baseLine, int w, int h, bool drawSelections, int fullSelectionWidth,
+ Q3TextStringChar *formatChar, const QPalette &pal,
+ bool rightToLeft);
+
+private:
+ QMap<int, Q3TextParagraphSelection> &selections() const;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ QList<Q3TextCustomItem *> &floatingItems() const;
+#endif
+ inline QBrush backgroundBrush(const QPalette &pal) {
+ if (bgcol)
+ return *bgcol;
+ return pal.brush(QPalette::Base);
+ }
+ void invalidateStyleCache();
+
+ QMap<int, QTextLineStart*> lineStarts;
+ QRect r;
+ Q3TextParagraph *p, *n;
+ void *docOrPseudo;
+ uint changed : 1;
+ uint firstFormat : 1;
+ uint firstPProcess : 1;
+ uint needPreProcess : 1;
+ uint fullWidth : 1;
+ uint lastInFrame : 1;
+ uint visible : 1;
+ uint breakable : 1;
+ uint movedDown : 1;
+ uint mightHaveCustomItems : 1;
+ uint hasdoc : 1;
+ uint litem : 1; // whether the paragraph is a list item
+ uint rtext : 1; // whether the paragraph needs rich text margin
+ signed int align : 5;
+ uint /*Q3StyleSheetItem::ListStyle*/ lstyle : 4;
+ int invalid;
+ int state, id;
+ Q3TextString *str;
+ QMap<int, Q3TextParagraphSelection> *mSelections;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ QList<Q3TextCustomItem *> *mFloatingItems;
+#endif
+ short utm, ubm, ulm, urm, uflm, ulinespacing;
+ short tabStopWidth, minwidth;
+ int *tArray;
+ Q3TextParagraphData *eData;
+ short list_val;
+ ushort ldepth;
+ QColor *bgcol;
+ QPaintDevice *paintdevice;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatter
+{
+public:
+ Q3TextFormatter();
+ virtual ~Q3TextFormatter();
+
+ virtual int format(Q3TextDocument *doc, Q3TextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts) = 0;
+ virtual int formatVertically(Q3TextDocument* doc, Q3TextParagraph* parag);
+
+ bool isWrapEnabled(Q3TextParagraph *p) const { if (!wrapEnabled) return false; if (p && !p->isBreakable()) return false; return true;}
+ int wrapAtColumn() const { return wrapColumn;}
+ virtual void setWrapEnabled(bool b);
+ virtual void setWrapAtColumn(int c);
+ virtual void setAllowBreakInWords(bool b) { biw = b; }
+ bool allowBreakInWords() const { return biw; }
+
+ int minimumWidth() const { return thisminw; }
+ int widthUsed() const { return thiswused; }
+
+protected:
+ virtual QTextLineStart *formatLine(Q3TextParagraph *parag, Q3TextString *string, QTextLineStart *line, Q3TextStringChar *start,
+ Q3TextStringChar *last, int align = Qt::AlignAuto, int space = 0);
+#ifndef QT_NO_COMPLEXTEXT
+ virtual QTextLineStart *bidiReorderLine(Q3TextParagraph *parag, Q3TextString *string, QTextLineStart *line, Q3TextStringChar *start,
+ Q3TextStringChar *last, int align, int space);
+#endif
+ void insertLineStart(Q3TextParagraph *parag, int index, QTextLineStart *ls);
+
+ int thisminw;
+ int thiswused;
+
+private:
+ bool wrapEnabled;
+ int wrapColumn;
+ bool biw;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatterBreakInWords : public Q3TextFormatter
+{
+public:
+ Q3TextFormatterBreakInWords();
+ virtual ~Q3TextFormatterBreakInWords() {}
+
+ int format(Q3TextDocument *doc, Q3TextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts);
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatterBreakWords : public Q3TextFormatter
+{
+public:
+ Q3TextFormatterBreakWords();
+ virtual ~Q3TextFormatterBreakWords() {}
+
+ int format(Q3TextDocument *doc, Q3TextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts);
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextIndent
+{
+public:
+ Q3TextIndent();
+ virtual ~Q3TextIndent() {}
+
+ virtual void indent(Q3TextDocument *doc, Q3TextParagraph *parag, int *oldIndent = 0, int *newIndent = 0) = 0;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextPreProcessor
+{
+public:
+ enum Ids {
+ Standard = 0
+ };
+
+ Q3TextPreProcessor();
+ virtual ~Q3TextPreProcessor() {}
+
+ virtual void process(Q3TextDocument *doc, Q3TextParagraph *, int, bool = true) = 0;
+ virtual Q3TextFormat *format(int id) = 0;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormat
+{
+ friend class Q3TextFormatCollection;
+ friend class Q3TextDocument;
+
+public:
+ enum Flags {
+ NoFlags,
+ Bold = 1,
+ Italic = 2,
+ Underline = 4,
+ Family = 8,
+ Size = 16,
+ Color = 32,
+ Misspelled = 64,
+ VAlign = 128,
+ StrikeOut= 256,
+ Font = Bold | Italic | Underline | Family | Size | StrikeOut,
+ Format = Font | Color | Misspelled | VAlign
+ };
+
+ enum VerticalAlignment { AlignNormal, AlignSuperScript, AlignSubScript };
+
+ Q3TextFormat();
+ virtual ~Q3TextFormat();
+
+ Q3TextFormat(const Q3StyleSheetItem *s);
+ Q3TextFormat(const QFont &f, const QColor &c, Q3TextFormatCollection *parent = 0);
+ Q3TextFormat(const Q3TextFormat &fm);
+ Q3TextFormat makeTextFormat(const Q3StyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor) const;
+ Q3TextFormat& operator=(const Q3TextFormat &fm);
+ QColor color() const;
+ QFont font() const;
+ QFontMetrics fontMetrics() const { return fm; }
+ bool isMisspelled() const;
+ VerticalAlignment vAlign() const;
+ int minLeftBearing() const;
+ int minRightBearing() const;
+ int width(const QChar &c) const;
+ int width(const QString &str, int pos) const;
+ int height() const;
+ int ascent() const;
+ int descent() const;
+ int leading() const;
+ bool useLinkColor() const;
+
+ void setBold(bool b);
+ void setItalic(bool b);
+ void setUnderline(bool b);
+ void setStrikeOut(bool b);
+ void setFamily(const QString &f);
+ void setPointSize(int s);
+ void setFont(const QFont &f);
+ void setColor(const QColor &c);
+ void setMisspelled(bool b);
+ void setVAlign(VerticalAlignment a);
+
+ bool operator==(const Q3TextFormat &f) const;
+ Q3TextFormatCollection *parent() const;
+ const QString &key() const;
+
+ static QString getKey(const QFont &f, const QColor &c, bool misspelled, VerticalAlignment vAlign);
+
+ void addRef();
+ void removeRef();
+
+ QString makeFormatChangeTags(Q3TextFormat* defaultFormat, Q3TextFormat *f, const QString& oldAnchorHref, const QString& anchorHref) const;
+ QString makeFormatEndTags(Q3TextFormat* defaultFormat, const QString& anchorHref) const;
+
+ static void setPainter(QPainter *p);
+ static QPainter* painter();
+
+ bool fontSizesInPixels() { return usePixelSizes; }
+
+protected:
+ virtual void generateKey();
+
+private:
+ void update();
+ static void applyFont(const QFont &f);
+
+private:
+ QFont fn;
+ QColor col;
+ QFontMetrics fm;
+ uint missp : 1;
+ uint linkColor : 1;
+ uint usePixelSizes : 1;
+ int leftBearing, rightBearing;
+ VerticalAlignment ha;
+ uchar widths[256];
+ int hei, asc, dsc;
+ Q3TextFormatCollection *collection;
+ int ref;
+ QString k;
+ int logicalFontSize;
+ int stdSize;
+ static QPainter *pntr;
+ static QFontMetrics *pntr_fm;
+ static int pntr_asc;
+ static int pntr_hei;
+ static int pntr_ldg;
+ static int pntr_dsc;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatCollection
+{
+ friend class Q3TextDocument;
+ friend class Q3TextFormat;
+
+public:
+ Q3TextFormatCollection();
+ virtual ~Q3TextFormatCollection();
+
+ void setDefaultFormat(Q3TextFormat *f);
+ Q3TextFormat *defaultFormat() const;
+ virtual Q3TextFormat *format(Q3TextFormat *f);
+ virtual Q3TextFormat *format(Q3TextFormat *of, Q3TextFormat *nf, int flags);
+ virtual Q3TextFormat *format(const QFont &f, const QColor &c);
+ virtual void remove(Q3TextFormat *f);
+ virtual Q3TextFormat *createFormat(const Q3TextFormat &f) { return new Q3TextFormat(f); }
+ virtual Q3TextFormat *createFormat(const QFont &f, const QColor &c) { return new Q3TextFormat(f, c, this); }
+
+ void updateDefaultFormat(const QFont &font, const QColor &c, Q3StyleSheet *sheet);
+
+ QPaintDevice *paintDevice() const { return paintdevice; }
+ void setPaintDevice(QPaintDevice *);
+
+private:
+ void updateKeys();
+
+private:
+ Q3TextFormat *defFormat, *lastFormat, *cachedFormat;
+ QHash<QString, Q3TextFormat *> cKey;
+ Q3TextFormat *cres;
+ QFont cfont;
+ QColor ccol;
+ QString kof, knf;
+ int cflags;
+
+ QPaintDevice *paintdevice;
+};
+
+class Q_COMPAT_EXPORT Q3TextParagraphPseudoDocument
+{
+public:
+ Q3TextParagraphPseudoDocument();
+ ~Q3TextParagraphPseudoDocument();
+ QRect docRect;
+ Q3TextFormatter *pFormatter;
+ Q3TextCommandHistory *commandHistory;
+ int minw;
+ int wused;
+ Q3TextFormatCollection collection;
+};
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline int Q3TextParagraph::length() const
+{
+ return str->length();
+}
+
+inline QRect Q3TextParagraph::rect() const
+{
+ return r;
+}
+
+inline int Q3TextCursor::index() const
+{
+ return idx;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline int Q3TextDocument::x() const
+{
+ return cx;
+}
+
+inline int Q3TextDocument::y() const
+{
+ return cy;
+}
+
+inline int Q3TextDocument::width() const
+{
+ return qMax(cw, flow_->width());
+}
+
+inline int Q3TextDocument::visibleWidth() const
+{
+ return vw;
+}
+
+inline Q3TextParagraph *Q3TextDocument::firstParagraph() const
+{
+ return fParag;
+}
+
+inline Q3TextParagraph *Q3TextDocument::lastParagraph() const
+{
+ return lParag;
+}
+
+inline void Q3TextDocument::setFirstParagraph(Q3TextParagraph *p)
+{
+ fParag = p;
+}
+
+inline void Q3TextDocument::setLastParagraph(Q3TextParagraph *p)
+{
+ lParag = p;
+}
+
+inline void Q3TextDocument::setWidth(int w)
+{
+ cw = qMax(w, minw);
+ flow_->setWidth(cw);
+ vw = w;
+}
+
+inline int Q3TextDocument::minimumWidth() const
+{
+ return minw;
+}
+
+inline void Q3TextDocument::setY(int y)
+{
+ cy = y;
+}
+
+inline int Q3TextDocument::leftMargin() const
+{
+ return leftmargin;
+}
+
+inline void Q3TextDocument::setLeftMargin(int lm)
+{
+ leftmargin = lm;
+}
+
+inline int Q3TextDocument::rightMargin() const
+{
+ return rightmargin;
+}
+
+inline void Q3TextDocument::setRightMargin(int rm)
+{
+ rightmargin = rm;
+}
+
+inline Q3TextPreProcessor *Q3TextDocument::preProcessor() const
+{
+ return pProcessor;
+}
+
+inline void Q3TextDocument::setPreProcessor(Q3TextPreProcessor * sh)
+{
+ pProcessor = sh;
+}
+
+inline void Q3TextDocument::setFormatter(Q3TextFormatter *f)
+{
+ delete pFormatter;
+ pFormatter = f;
+}
+
+inline Q3TextFormatter *Q3TextDocument::formatter() const
+{
+ return pFormatter;
+}
+
+inline void Q3TextDocument::setIndent(Q3TextIndent *i)
+{
+ indenter = i;
+}
+
+inline Q3TextIndent *Q3TextDocument::indent() const
+{
+ return indenter;
+}
+
+inline QColor Q3TextDocument::selectionColor(int id) const
+{
+ const Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ return p->selectionColors[id].background;
+}
+
+inline QColor Q3TextDocument::selectionTextColor(int id) const
+{
+ const Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ return p->selectionColors[id].text;
+}
+
+inline bool Q3TextDocument::hasSelectionTextColor(int id) const
+{
+ const Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ return p->selectionColors.contains(id);
+}
+
+inline void Q3TextDocument::setSelectionColor(int id, const QColor &c)
+{
+ Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ p->selectionColors[id].background = c;
+}
+
+inline void Q3TextDocument::setSelectionTextColor(int id, const QColor &c)
+{
+ Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ p->selectionColors[id].text = c;
+}
+
+inline Q3TextFormatCollection *Q3TextDocument::formatCollection() const
+{
+ return fCollection;
+}
+
+inline int Q3TextDocument::alignment() const
+{
+ return align;
+}
+
+inline void Q3TextDocument::setAlignment(int a)
+{
+ align = a;
+}
+
+inline int *Q3TextDocument::tabArray() const
+{
+ return tArray;
+}
+
+inline int Q3TextDocument::tabStopWidth() const
+{
+ return tStopWidth;
+}
+
+inline void Q3TextDocument::setTabArray(int *a)
+{
+ tArray = a;
+}
+
+inline void Q3TextDocument::setTabStops(int tw)
+{
+ tStopWidth = tw;
+}
+
+inline QString Q3TextDocument::originalText() const
+{
+ if (oTextValid)
+ return oText;
+ return text();
+}
+
+inline void Q3TextDocument::setFlow(Q3TextFlow *f)
+{
+ if (flow_)
+ delete flow_;
+ flow_ = f;
+}
+
+inline void Q3TextDocument::takeFlow()
+{
+ flow_ = 0;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline QColor Q3TextFormat::color() const
+{
+ return col;
+}
+
+inline QFont Q3TextFormat::font() const
+{
+ return fn;
+}
+
+inline bool Q3TextFormat::isMisspelled() const
+{
+ return missp;
+}
+
+inline Q3TextFormat::VerticalAlignment Q3TextFormat::vAlign() const
+{
+ return ha;
+}
+
+inline bool Q3TextFormat::operator==(const Q3TextFormat &f) const
+{
+ return k == f.k;
+}
+
+inline Q3TextFormatCollection *Q3TextFormat::parent() const
+{
+ return collection;
+}
+
+inline void Q3TextFormat::addRef()
+{
+ ref++;
+}
+
+inline void Q3TextFormat::removeRef()
+{
+ ref--;
+ if (!collection)
+ return;
+ if (this == collection->defFormat)
+ return;
+ if (ref == 0)
+ collection->remove(this);
+}
+
+inline const QString &Q3TextFormat::key() const
+{
+ return k;
+}
+
+inline bool Q3TextFormat::useLinkColor() const
+{
+ return linkColor;
+}
+
+inline Q3TextStringChar *Q3TextParagraph::at(int i) const
+{
+ return &str->at(i);
+}
+
+inline bool Q3TextParagraph::isValid() const
+{
+ return invalid == -1;
+}
+
+inline bool Q3TextParagraph::hasChanged() const
+{
+ return changed;
+}
+
+inline void Q3TextParagraph::setBackgroundColor(const QColor & c)
+{
+ delete bgcol;
+ bgcol = new QColor(c);
+ setChanged(true);
+}
+
+inline void Q3TextParagraph::clearBackgroundColor()
+{
+ delete bgcol; bgcol = 0; setChanged(true);
+}
+
+inline void Q3TextParagraph::append(const QString &s, bool reallyAtEnd)
+{
+ if (reallyAtEnd) {
+ insert(str->length(), s);
+ } else {
+ int str_end = str->length() - 1;
+ insert(str_end > 0 ? str_end : 0, s);
+ }
+}
+
+inline Q3TextParagraph *Q3TextParagraph::prev() const
+{
+ return p;
+}
+
+inline Q3TextParagraph *Q3TextParagraph::next() const
+{
+ return n;
+}
+
+inline bool Q3TextParagraph::hasAnySelection() const
+{
+ return mSelections ? !selections().isEmpty() : false;
+}
+
+inline void Q3TextParagraph::setEndState(int s)
+{
+ if (s == state)
+ return;
+ state = s;
+}
+
+inline int Q3TextParagraph::endState() const
+{
+ return state;
+}
+
+inline void Q3TextParagraph::setParagId(int i)
+{
+ id = i;
+}
+
+inline int Q3TextParagraph::paragId() const
+{
+ if (id == -1)
+ qWarning("invalid parag id!!!!!!!! (%p)", (void*)this);
+ return id;
+}
+
+inline bool Q3TextParagraph::firstPreProcess() const
+{
+ return firstPProcess;
+}
+
+inline void Q3TextParagraph::setFirstPreProcess(bool b)
+{
+ firstPProcess = b;
+}
+
+inline QMap<int, QTextLineStart*> &Q3TextParagraph::lineStartList()
+{
+ return lineStarts;
+}
+
+inline Q3TextString *Q3TextParagraph::string() const
+{
+ return str;
+}
+
+inline Q3TextParagraphPseudoDocument *Q3TextParagraph::pseudoDocument() const
+{
+ if (hasdoc)
+ return 0;
+ return (Q3TextParagraphPseudoDocument*) docOrPseudo;
+}
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+inline Q3TextTableCell *Q3TextParagraph::tableCell() const
+{
+ return hasdoc ? document()->tableCell () : 0;
+}
+#endif
+
+inline Q3TextCommandHistory *Q3TextParagraph::commands() const
+{
+ return hasdoc ? document()->commands() : pseudoDocument()->commandHistory;
+}
+
+
+inline int Q3TextParagraph::alignment() const
+{
+ return align;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+inline void Q3TextParagraph::registerFloatingItem(Q3TextCustomItem *i)
+{
+ floatingItems().append(i);
+}
+
+inline void Q3TextParagraph::unregisterFloatingItem(Q3TextCustomItem *i)
+{
+ floatingItems().removeAll(i);
+}
+#endif
+
+inline QBrush *Q3TextParagraph::background() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return tableCell() ? tableCell()->backGround() : 0;
+#else
+ return 0;
+#endif
+}
+
+inline int Q3TextParagraph::documentWidth() const
+{
+ return hasdoc ? document()->width() : pseudoDocument()->docRect.width();
+}
+
+inline int Q3TextParagraph::documentVisibleWidth() const
+{
+ return hasdoc ? document()->visibleWidth() : pseudoDocument()->docRect.width();
+}
+
+inline int Q3TextParagraph::documentX() const
+{
+ return hasdoc ? document()->x() : pseudoDocument()->docRect.x();
+}
+
+inline int Q3TextParagraph::documentY() const
+{
+ return hasdoc ? document()->y() : pseudoDocument()->docRect.y();
+}
+
+inline void Q3TextParagraph::setExtraData(Q3TextParagraphData *data)
+{
+ eData = data;
+}
+
+inline Q3TextParagraphData *Q3TextParagraph::extraData() const
+{
+ return eData;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline void Q3TextFormatCollection::setDefaultFormat(Q3TextFormat *f)
+{
+ defFormat = f;
+}
+
+inline Q3TextFormat *Q3TextFormatCollection::defaultFormat() const
+{
+ return defFormat;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline Q3TextFormat *Q3TextStringChar::format() const
+{
+ return (type == Regular) ? p.format : p.custom->format;
+}
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+inline Q3TextCustomItem *Q3TextStringChar::customItem() const
+{
+ return isCustom() ? p.custom->custom : 0;
+}
+#endif
+
+inline int Q3TextStringChar::height() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return !isCustom() ? format()->height() : (customItem()->placement() == Q3TextCustomItem::PlaceInline ? customItem()->height : 0);
+#else
+ return format()->height();
+#endif
+}
+
+inline int Q3TextStringChar::ascent() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return !isCustom() ? format()->ascent() : (customItem()->placement() == Q3TextCustomItem::PlaceInline ? customItem()->ascent() : 0);
+#else
+ return format()->ascent();
+#endif
+}
+
+inline int Q3TextStringChar::descent() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return !isCustom() ? format()->descent() : 0;
+#else
+ return format()->descent();
+#endif
+}
+
+#endif // QT_NO_RICHTEXT
+
+QT_END_NAMESPACE
+
+#endif // Q3RICHTEXT_P_H
diff --git a/src/qt3support/text/q3simplerichtext.cpp b/src/qt3support/text/q3simplerichtext.cpp
new file mode 100644
index 0000000..020b7db
--- /dev/null
+++ b/src/qt3support/text/q3simplerichtext.cpp
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3simplerichtext.h"
+
+#ifndef QT_NO_RICHTEXT
+#include "q3richtext_p.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SimpleRichTextData
+{
+public:
+ Q3TextDocument *doc;
+ QFont font;
+ int cachedWidth;
+ bool cachedWidthWithPainter;
+ void adjustSize();
+};
+
+// Pull this private function in from qglobal.cpp
+Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
+
+void Q3SimpleRichTextData::adjustSize() {
+ QFontMetrics fm(font);
+ int mw = fm.width(QString(QLatin1Char('x'))) * 80;
+ int w = mw;
+ doc->doLayout(0,w);
+ if (doc->widthUsed() != 0) {
+ w = qt_int_sqrt(5 * doc->height() * doc->widthUsed() / 3);
+ doc->doLayout(0, qMin(w, mw));
+
+ if (w*3 < 5*doc->height()) {
+ w = qt_int_sqrt(2 * doc->height() * doc->widthUsed());
+ doc->doLayout(0,qMin(w, mw));
+ }
+ }
+ cachedWidth = doc->width();
+ cachedWidthWithPainter = false;
+}
+
+/*!
+ \class Q3SimpleRichText
+ \brief The Q3SimpleRichText class provides a small displayable piece of rich text.
+
+ \compat
+
+ This class encapsulates simple rich text usage in which a string
+ is interpreted as rich text and can be drawn. This is particularly
+ useful if you want to display some rich text in a custom widget. A
+ Q3StyleSheet is needed to interpret the tags and format the rich
+ text. Qt provides a default HTML-like style sheet, but you may
+ define custom style sheets.
+
+ Once created, the rich text object can be queried for its width(),
+ height(), and the actual width used (see widthUsed()). Most
+ importantly, it can be drawn on any given QPainter with draw().
+ Q3SimpleRichText can also be used to implement hypertext or active
+ text facilities by using anchorAt(). A hit test through inText()
+ makes it possible to use simple rich text for text objects in
+ editable drawing canvases.
+
+ Once constructed from a string the contents cannot be changed,
+ only resized. If the contents change, just throw the rich text
+ object away and make a new one with the new contents.
+
+ For large documents use QTextEdit or QTextBrowser. For very small
+ items of rich text you can use a QLabel.
+
+ If you are using Q3SimpleRichText to print in high resolution you
+ should call setWidth(QPainter, int) so that the content will be
+ laid out properly on the page.
+*/
+
+/*!
+ Constructs a Q3SimpleRichText from the rich text string \a text and
+ the font \a fnt.
+
+ The font is used as a basis for the text rendering. When using
+ rich text rendering on a widget \e w, you would normally specify
+ the widget's font, for example:
+
+ \snippet doc/src/snippets/code/src_qt3support_text_q3simplerichtext.cpp 0
+
+ \a context is the optional context of the rich text object. This
+ becomes important if \a text contains relative references, for
+ example within image tags. Q3SimpleRichText always uses the default
+ mime source factory (see \l{Q3MimeSourceFactory::defaultFactory()})
+ to resolve those references. The context will then be used to
+ calculate the absolute path. See
+ Q3MimeSourceFactory::makeAbsolute() for details.
+
+ The \a sheet is an optional style sheet. If it is 0, the default
+ style sheet will be used (see \l{Q3StyleSheet::defaultSheet()}).
+*/
+
+Q3SimpleRichText::Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context, const Q3StyleSheet* sheet)
+{
+ d = new Q3SimpleRichTextData;
+ d->cachedWidth = -1;
+ d->cachedWidthWithPainter = false;
+ d->font = fnt;
+ d->doc = new Q3TextDocument(0);
+ d->doc->setTextFormat(Qt::RichText);
+ d->doc->setLeftMargin(0);
+ d->doc->setRightMargin(0);
+ d->doc->setFormatter(new Q3TextFormatterBreakWords);
+ d->doc->setStyleSheet((Q3StyleSheet*)sheet);
+ d->doc->setDefaultFormat(fnt, QColor());
+ d->doc->setText(text, context);
+}
+
+
+/*!
+ Constructs a Q3SimpleRichText from the rich text string \a text and
+ the font \a fnt.
+
+ This is a slightly more complex constructor for Q3SimpleRichText
+ that takes an additional mime source factory \a factory, a page
+ break parameter \a pageBreak and a bool \a linkUnderline. \a
+ linkColor is only provided for compatibility, but has no effect,
+ as QPalette::link() color is used now.
+
+ \a context is the optional context of the rich text object. This
+ becomes important if \a text contains relative references, for
+ example within image tags. Q3SimpleRichText always uses the default
+ mime source factory (see \l{Q3MimeSourceFactory::defaultFactory()})
+ to resolve those references. The context will then be used to
+ calculate the absolute path. See
+ Q3MimeSourceFactory::makeAbsolute() for details.
+
+ The \a sheet is an optional style sheet. If it is 0, the default
+ style sheet will be used (see \l{Q3StyleSheet::defaultSheet()}).
+
+ This constructor is useful for creating a Q3SimpleRichText object
+ suitable for printing. Set \a pageBreak to be the height of the
+ contents area of the pages.
+*/
+
+Q3SimpleRichText::Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context, const Q3StyleSheet* sheet,
+ const Q3MimeSourceFactory* factory, int pageBreak,
+ const QColor& /*linkColor*/, bool linkUnderline)
+{
+ d = new Q3SimpleRichTextData;
+ d->cachedWidth = -1;
+ d->cachedWidthWithPainter = false;
+ d->font = fnt;
+ d->doc = new Q3TextDocument(0);
+ d->doc->setTextFormat(Qt::RichText);
+ d->doc->setFormatter(new Q3TextFormatterBreakWords);
+ d->doc->setStyleSheet((Q3StyleSheet*)sheet);
+ d->doc->setDefaultFormat(fnt, QColor());
+ d->doc->flow()->setPageSize(pageBreak);
+ d->doc->setPageBreakEnabled(true);
+#ifndef QT_NO_MIME
+ d->doc->setMimeSourceFactory((Q3MimeSourceFactory*)factory);
+#endif
+ d->doc->setUnderlineLinks(linkUnderline);
+ d->doc->setText(text, context);
+}
+
+/*!
+ Destroys the rich text object, freeing memory.
+*/
+
+Q3SimpleRichText::~Q3SimpleRichText()
+{
+ delete d->doc;
+ delete d;
+}
+
+/*!
+ \overload
+
+ Sets the width of the rich text object to \a w pixels.
+
+ \sa height(), adjustSize()
+*/
+
+void Q3SimpleRichText::setWidth(int w)
+{
+ if (w == d->cachedWidth && !d->cachedWidthWithPainter)
+ return;
+ d->doc->formatter()->setAllowBreakInWords(d->doc->isPageBreakEnabled());
+ d->cachedWidth = w;
+ d->cachedWidthWithPainter = false;
+ d->doc->doLayout(0, w);
+}
+
+/*!
+ Sets the width of the rich text object to \a w pixels,
+ recalculating the layout as if it were to be drawn with painter \a
+ p.
+
+ Passing a painter is useful when you intend drawing on devices
+ other than the screen, for example a QPrinter.
+
+ \sa height(), adjustSize()
+*/
+
+void Q3SimpleRichText::setWidth(QPainter *p, int w)
+{
+ if (w == d->cachedWidth && d->cachedWidthWithPainter)
+ return;
+ d->doc->formatter()->setAllowBreakInWords(d->doc->isPageBreakEnabled() ||
+ (p && p->device() &&
+ p->device()->devType() == QInternal::Printer));
+ p->save();
+ d->cachedWidth = w;
+ d->cachedWidthWithPainter = true;
+ d->doc->doLayout(p, w);
+ p->restore();
+}
+
+/*!
+ Returns the set width of the rich text object in pixels.
+
+ \sa widthUsed()
+*/
+
+int Q3SimpleRichText::width() const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ return d->doc->width();
+}
+
+/*!
+ Returns the width in pixels that is actually used by the rich text
+ object. This can be smaller or wider than the set width.
+
+ It may be wider, for example, if the text contains images or
+ non-breakable words that are already wider than the available
+ space. It's smaller when the object only consists of lines that do
+ not fill the width completely.
+
+ \sa width()
+*/
+
+int Q3SimpleRichText::widthUsed() const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ return d->doc->widthUsed();
+}
+
+/*!
+ Returns the height of the rich text object in pixels.
+
+ \sa setWidth()
+*/
+
+int Q3SimpleRichText::height() const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ return d->doc->height();
+}
+
+/*!
+ Adjusts the rich text object to a reasonable size.
+
+ \sa setWidth()
+*/
+
+void Q3SimpleRichText::adjustSize()
+{
+ d->adjustSize();
+}
+
+/*!
+ Draws the formatted text with painter \a p, at position (\a x, \a
+ y), clipped to \a clipRect. The clipping rectangle is given in the
+ rich text object's coordinates translated by (\a x, \a y). Passing
+ an null rectangle results in no clipping. Colors from the color
+ group \a cg are used as needed, and if not 0, *\a{paper} is
+ used as the background brush.
+
+ Note that the display code is highly optimized to reduce flicker,
+ so passing a brush for \a paper is preferable to simply clearing
+ the area to be painted and then calling this without a brush.
+*/
+
+void Q3SimpleRichText::draw(QPainter *p, int x, int y, const QRect& clipRect,
+ const QColorGroup &cg, const QBrush* paper) const
+{
+ p->save();
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ QRect r = clipRect;
+ if (!r.isNull())
+ r.moveBy(-x, -y);
+
+ if (paper)
+ d->doc->setPaper(new QBrush(*paper));
+ QPalette pal2 = cg;
+ if (d->doc->paper())
+ pal2.setBrush(QPalette::Base, *d->doc->paper());
+
+ if (!clipRect.isNull())
+ p->setClipRect(clipRect);
+ p->translate(x, y);
+ d->doc->draw(p, r, pal2, paper);
+ p->translate(-x, -y);
+ p->restore();
+}
+
+
+/*! \fn void Q3SimpleRichText::draw(QPainter *p, int x, int y, const QRegion& clipRegion,
+ const QColorGroup &cg, const QBrush* paper) const
+
+ Use the version with clipRect instead of this \a clipRegion version,
+ since this region version has problems with larger documents on some
+ platforms (on X11 regions internally are represented with 16-bit
+ coordinates).
+*/
+
+
+
+/*!
+ Returns the context of the rich text object. If no context has
+ been specified in the constructor, an empty string is returned. The
+ context is the path to use to look up relative links, such as
+ image tags and anchor references.
+*/
+
+QString Q3SimpleRichText::context() const
+{
+ return d->doc->context();
+}
+
+/*!
+ Returns the anchor at the requested position, \a pos. An empty
+ string is returned if no anchor is specified for this position.
+*/
+
+QString Q3SimpleRichText::anchorAt(const QPoint& pos) const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ Q3TextCursor c(d->doc);
+ c.place(pos, d->doc->firstParagraph(), true);
+ return c.paragraph()->at(c.index())->anchorHref();
+}
+
+/*!
+ Returns true if \a pos is within a text line of the rich text
+ object; otherwise returns false.
+*/
+
+bool Q3SimpleRichText::inText(const QPoint& pos) const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ if (pos.y() > d->doc->height())
+ return false;
+ Q3TextCursor c(d->doc);
+ c.place(pos, d->doc->firstParagraph());
+ return c.totalOffsetX() + c.paragraph()->at(c.index())->x +
+ c.paragraph()->at(c.index())->format()->width(c.paragraph()->at(c.index())->c) > pos.x();
+}
+
+/*!
+ Sets the default font for the rich text object to \a f
+*/
+
+void Q3SimpleRichText::setDefaultFont(const QFont &f)
+{
+ if (d->font == f)
+ return;
+ d->font = f;
+ d->cachedWidth = -1;
+ d->cachedWidthWithPainter = false;
+ d->doc->setDefaultFormat(f, QColor());
+ d->doc->setText(d->doc->originalText(), d->doc->context());
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3simplerichtext.h b/src/qt3support/text/q3simplerichtext.h
new file mode 100644
index 0000000..a36e902
--- /dev/null
+++ b/src/qt3support/text/q3simplerichtext.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SIMPLERICHTEXT_H
+#define Q3SIMPLERICHTEXT_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_RICHTEXT
+
+class QPainter;
+class QWidget;
+class Q3StyleSheet;
+class QBrush;
+class Q3MimeSourceFactory;
+class Q3SimpleRichTextData;
+
+class Q_COMPAT_EXPORT Q3SimpleRichText
+{
+public:
+ Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context = QString(), const Q3StyleSheet* sheet = 0);
+ Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context, const Q3StyleSheet *sheet,
+ const Q3MimeSourceFactory* factory, int pageBreak = -1,
+ const QColor& linkColor = Qt::blue, bool linkUnderline = true);
+ ~Q3SimpleRichText();
+
+ void setWidth(int);
+ void setWidth(QPainter*, int);
+ void setDefaultFont(const QFont &f);
+ int width() const;
+ int widthUsed() const;
+ int height() const;
+ void adjustSize();
+
+ void draw(QPainter* p, int x, int y, const QRect& clipRect,
+ const QColorGroup& cg, const QBrush* paper = 0) const;
+
+ void draw(QPainter* p, int x, int y, const QRegion& clipRegion,
+ const QColorGroup& cg, const QBrush* paper = 0) const {
+ draw(p, x, y, clipRegion.boundingRect(), cg, paper);
+ }
+
+ QString context() const;
+ QString anchorAt(const QPoint& pos) const;
+
+ bool inText(const QPoint& pos) const;
+
+private:
+ Q_DISABLE_COPY(Q3SimpleRichText)
+
+ Q3SimpleRichTextData* d;
+};
+
+#endif // QT_NO_RICHTEXT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SIMPLERICHTEXT_H
diff --git a/src/qt3support/text/q3stylesheet.cpp b/src/qt3support/text/q3stylesheet.cpp
new file mode 100644
index 0000000..5f89b0c
--- /dev/null
+++ b/src/qt3support/text/q3stylesheet.cpp
@@ -0,0 +1,1471 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3stylesheet.h"
+
+#ifndef QT_NO_RICHTEXT
+
+#include "qlayout.h"
+#include "qpainter.h"
+#include "q3cleanuphandler.h"
+#include <qtextdocument.h>
+
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3StyleSheetItemData
+{
+public:
+ Q3StyleSheetItem::DisplayMode disp;
+ int fontitalic;
+ int fontunderline;
+ int fontstrikeout;
+ int fontweight;
+ int fontsize;
+ int fontsizelog;
+ int fontsizestep;
+ int lineSpacing;
+ QString fontfamily;
+ Q3StyleSheetItem *parentstyle;
+ QString stylename;
+ int ncolumns;
+ QColor col;
+ bool anchor;
+ int align;
+ Q3StyleSheetItem::VerticalAlignment valign;
+ int margin[5];
+ Q3StyleSheetItem::ListStyle list;
+ Q3StyleSheetItem::WhiteSpaceMode whitespacemode;
+ QString contxt;
+ bool selfnest;
+ Q3StyleSheet* sheet;
+};
+
+/*!
+ \class Q3StyleSheetItem
+ \brief The Q3StyleSheetItem class provides an encapsulation of a set of text styles.
+
+ \compat
+
+ A style sheet item consists of a name and a set of attributes that
+ specify its font, color, etc. When used in a \link Q3StyleSheet
+ style sheet\endlink (see styleSheet()), items define the name() of
+ a rich text tag and the display property changes associated with
+ it.
+
+ The \link Q3StyleSheetItem::DisplayMode display mode\endlink
+ attribute indicates whether the item is a block, an inline element
+ or a list element; see setDisplayMode(). The treatment of
+ whitespace is controlled by the \link
+ Q3StyleSheetItem::WhiteSpaceMode white space mode\endlink; see
+ setWhiteSpaceMode(). An item's margins are set with setMargin(),
+ In the case of list items, the list style is set with
+ setListStyle(). An item may be a hypertext link anchor; see
+ setAnchor(). Other attributes are set with setAlignment(),
+ setVerticalAlignment(), setFontFamily(), setFontSize(),
+ setFontWeight(), setFontItalic(), setFontUnderline(),
+ setFontStrikeOut and setColor().
+*/
+
+/*! \enum Q3StyleSheetItem::AdditionalStyleValues
+ \internal
+*/
+
+/*!
+ \enum Q3StyleSheetItem::WhiteSpaceMode
+
+ This enum defines the ways in which Q3StyleSheet can treat
+ whitespace.
+
+ \value WhiteSpaceNormal any sequence of whitespace (including
+ line-breaks) is equivalent to a single space.
+
+ \value WhiteSpacePre whitespace must be output exactly as given
+ in the input.
+
+ \value WhiteSpaceNoWrap multiple spaces are collapsed as with
+ WhiteSpaceNormal, but no automatic line-breaks occur. To break
+ lines manually, use the \c{<br>} tag.
+
+ \omitvalue WhiteSpaceModeUndefined
+*/
+
+/*!
+ \enum Q3StyleSheetItem::Margin
+
+ \value MarginLeft left margin
+ \value MarginRight right margin
+ \value MarginTop top margin
+ \value MarginBottom bottom margin
+ \value MarginAll all margins (left, right, top and bottom)
+ \value MarginVertical top and bottom margins
+ \value MarginHorizontal left and right margins
+ \value MarginFirstLine margin (indentation) of the first line of
+ a paragarph (in addition to the MarginLeft of the paragraph)
+ \value MarginUndefined
+*/
+
+/*!
+ Constructs a new style called \a name for the stylesheet \a
+ parent.
+
+ All properties in Q3StyleSheetItem are initially in the "do not
+ change" state, except \link Q3StyleSheetItem::DisplayMode display
+ mode\endlink, which defaults to \c DisplayInline.
+*/
+Q3StyleSheetItem::Q3StyleSheetItem(Q3StyleSheet* parent, const QString& name)
+{
+ d = new Q3StyleSheetItemData;
+ d->stylename = name.toLower();
+ d->sheet = parent;
+ init();
+ if (parent)
+ parent->insert(this);
+}
+
+/*!
+ Copy constructor. Constructs a copy of \a other that is not bound
+ to any style sheet.
+*/
+Q3StyleSheetItem::Q3StyleSheetItem(const Q3StyleSheetItem & other)
+{
+ d = new Q3StyleSheetItemData;
+ *d = *other.d;
+}
+
+
+/*!
+ Destroys the style. Note that Q3StyleSheetItem objects become
+ owned by Q3StyleSheet when they are created.
+*/
+Q3StyleSheetItem::~Q3StyleSheetItem()
+{
+ delete d;
+}
+
+/*!
+ Assignment. Assings a copy of \a other that is not bound to any style sheet.
+ Unbounds first from previous style sheet.
+ */
+Q3StyleSheetItem& Q3StyleSheetItem::operator=(const Q3StyleSheetItem& other)
+{
+ if (&other == this)
+ return *this;
+ delete d;
+ d = new Q3StyleSheetItemData;
+ *d = *other.d;
+ return *this;
+}
+
+/*!
+ Returns the style sheet this item is in.
+*/
+Q3StyleSheet* Q3StyleSheetItem::styleSheet()
+{
+ return d->sheet;
+}
+
+/*!
+ \overload
+
+ Returns the style sheet this item is in.
+*/
+const Q3StyleSheet* Q3StyleSheetItem::styleSheet() const
+{
+ return d->sheet;
+}
+
+/*!
+ \internal
+ Internal initialization
+ */
+void Q3StyleSheetItem::init()
+{
+ d->disp = DisplayInline;
+
+ d->fontitalic = Undefined;
+ d->fontunderline = Undefined;
+ d->fontstrikeout = Undefined;
+ d->fontweight = Undefined;
+ d->fontsize = Undefined;
+ d->fontsizelog = Undefined;
+ d->fontsizestep = 0;
+ d->ncolumns = Undefined;
+ d->col = QColor(); // !isValid()
+ d->anchor = false;
+ d->align = Undefined;
+ d->valign = VAlignBaseline;
+ d->margin[0] = Undefined;
+ d->margin[1] = Undefined;
+ d->margin[2] = Undefined;
+ d->margin[3] = Undefined;
+ d->margin[4] = Undefined;
+ d->list = ListStyleUndefined;
+ d->whitespacemode = WhiteSpaceModeUndefined;
+ d->selfnest = true;
+ d->lineSpacing = Undefined;
+}
+
+/*!
+ Returns the name of the style item.
+*/
+QString Q3StyleSheetItem::name() const
+{
+ return d->stylename;
+}
+
+/*!
+ Returns the \link Q3StyleSheetItem::DisplayMode display
+ mode\endlink of the style.
+
+ \sa setDisplayMode()
+*/
+Q3StyleSheetItem::DisplayMode Q3StyleSheetItem::displayMode() const
+{
+ return d->disp;
+}
+
+/*!
+ \enum Q3StyleSheetItem::DisplayMode
+
+ This enum type defines the way adjacent elements are displayed.
+
+ \value DisplayBlock elements are displayed as a rectangular block
+ (e.g. \c{<p>...</p>}).
+
+ \value DisplayInline elements are displayed in a horizontally
+ flowing sequence (e.g. \c{<em>...</em>}).
+
+ \value DisplayListItem elements are displayed in a vertical
+ sequence (e.g. \c{<li>...</li>}).
+
+ \value DisplayNone elements are not displayed at all.
+
+ \omitvalue DisplayModeUndefined
+*/
+
+/*!
+ Sets the display mode of the style to \a m.
+
+ \sa displayMode()
+ */
+void Q3StyleSheetItem::setDisplayMode(DisplayMode m)
+{
+ d->disp=m;
+}
+
+
+/*!
+ Returns the alignment of this style. Possible values are
+ Qt::AlignAuto, Qt::AlignLeft, Qt::AlignRight, Qt::AlignCenter or
+ Qt::AlignJustify.
+
+ \sa setAlignment(), Qt::Alignment
+*/
+int Q3StyleSheetItem::alignment() const
+{
+ return d->align;
+}
+
+/*!
+ Sets the alignment to \a f. This only makes sense for styles with
+ a \link Q3StyleSheetItem::DisplayMode display mode\endlink of
+ DisplayBlock. Possible values are Qt::AlignAuto, Qt::AlignLeft,
+ Qt::AlignRight, Qt::AlignCenter or Qt::AlignJustify.
+
+ \sa alignment(), displayMode(), Qt::Alignment
+*/
+void Q3StyleSheetItem::setAlignment(int f)
+{
+ d->align = f;
+}
+
+
+/*!
+ Returns the vertical alignment of the style. Possible values are
+ VAlignBaseline, VAlignSub or VAlignSuper.
+
+ \sa setVerticalAlignment()
+*/
+Q3StyleSheetItem::VerticalAlignment Q3StyleSheetItem::verticalAlignment() const
+{
+ return d->valign;
+}
+
+/*!
+ \enum Q3StyleSheetItem::VerticalAlignment
+
+ This enum type defines the way elements are aligned vertically.
+ This is only supported for text elements.
+
+ \value VAlignBaseline align the baseline of the element (or the
+ bottom, if the element doesn't have a baseline) with the
+ baseline of the parent
+
+ \value VAlignSub subscript the element
+
+ \value VAlignSuper superscript the element
+
+*/
+
+
+/*!
+ Sets the vertical alignment to \a valign. Possible values are
+ VAlignBaseline, VAlignSub or VAlignSuper.
+
+ The vertical alignment property is not inherited.
+
+ \sa verticalAlignment()
+*/
+void Q3StyleSheetItem::setVerticalAlignment(VerticalAlignment valign)
+{
+ d->valign = valign;
+}
+
+
+/*!
+ Returns true if the style sets an italic font; otherwise returns
+ false.
+
+ \sa setFontItalic(), definesFontItalic()
+*/
+bool Q3StyleSheetItem::fontItalic() const
+{
+ return d->fontitalic > 0;
+}
+
+/*!
+ If \a italic is true sets italic for the style; otherwise sets
+ upright.
+
+ \sa fontItalic(), definesFontItalic()
+*/
+void Q3StyleSheetItem::setFontItalic(bool italic)
+{
+ d->fontitalic = italic?1:0;
+}
+
+/*!
+ Returns true if the style defines a font shape; otherwise returns
+ false. A style does not define any shape until setFontItalic() is
+ called.
+
+ \sa setFontItalic(), fontItalic()
+*/
+bool Q3StyleSheetItem::definesFontItalic() const
+{
+ return d->fontitalic != Undefined;
+}
+
+/*!
+ Returns true if the style sets an underlined font; otherwise
+ returns false.
+
+ \sa setFontUnderline(), definesFontUnderline()
+*/
+bool Q3StyleSheetItem::fontUnderline() const
+{
+ return d->fontunderline > 0;
+}
+
+/*!
+ If \a underline is true, sets underline for the style; otherwise
+ sets no underline.
+
+ \sa fontUnderline(), definesFontUnderline()
+*/
+void Q3StyleSheetItem::setFontUnderline(bool underline)
+{
+ d->fontunderline = underline?1:0;
+}
+
+/*!
+ Returns true if the style defines a setting for the underline
+ property of the font; otherwise returns false. A style does not
+ define this until setFontUnderline() is called.
+
+ \sa setFontUnderline(), fontUnderline()
+*/
+bool Q3StyleSheetItem::definesFontUnderline() const
+{
+ return d->fontunderline != Undefined;
+}
+
+
+/*!
+ Returns true if the style sets a strike out font; otherwise
+ returns false.
+
+ \sa setFontStrikeOut(), definesFontStrikeOut()
+*/
+bool Q3StyleSheetItem::fontStrikeOut() const
+{
+ return d->fontstrikeout > 0;
+}
+
+/*!
+ If \a strikeOut is true, sets strike out for the style; otherwise
+ sets no strike out.
+
+ \sa fontStrikeOut(), definesFontStrikeOut()
+*/
+void Q3StyleSheetItem::setFontStrikeOut(bool strikeOut)
+{
+ d->fontstrikeout = strikeOut?1:0;
+}
+
+/*!
+ Returns true if the style defines a setting for the strikeOut
+ property of the font; otherwise returns false. A style does not
+ define this until setFontStrikeOut() is called.
+
+ \sa setFontStrikeOut(), fontStrikeOut()
+*/
+bool Q3StyleSheetItem::definesFontStrikeOut() const
+{
+ return d->fontstrikeout != Undefined;
+}
+
+
+/*!
+ Returns the font weight setting of the style. This is either a
+ valid QFont::Weight or the value Q3StyleSheetItem::Undefined.
+
+ \sa setFontWeight(), QFont
+*/
+int Q3StyleSheetItem::fontWeight() const
+{
+ return d->fontweight;
+}
+
+/*!
+ Sets the font weight setting of the style to \a w. Valid values
+ are those defined by QFont::Weight.
+
+ \sa QFont, fontWeight()
+*/
+void Q3StyleSheetItem::setFontWeight(int w)
+{
+ d->fontweight = w;
+}
+
+/*!
+ Returns the logical font size setting of the style. This is either
+ a valid size between 1 and 7 or Q3StyleSheetItem::Undefined.
+
+ \sa setLogicalFontSize(), setLogicalFontSizeStep(), QFont::pointSize(), QFont::setPointSize()
+*/
+int Q3StyleSheetItem::logicalFontSize() const
+{
+ return d->fontsizelog;
+}
+
+
+/*!
+ Sets the logical font size setting of the style to \a s. Valid
+ logical sizes are 1 to 7.
+
+ \sa logicalFontSize(), QFont::pointSize(), QFont::setPointSize()
+*/
+void Q3StyleSheetItem::setLogicalFontSize(int s)
+{
+ d->fontsizelog = s;
+}
+
+/*!
+ Returns the logical font size step of this style.
+
+ The default is 0. Tags such as \c big define \c +1; \c small
+ defines \c -1.
+
+ \sa setLogicalFontSizeStep()
+*/
+int Q3StyleSheetItem::logicalFontSizeStep() const
+{
+ return d->fontsizestep;
+}
+
+/*!
+ Sets the logical font size step of this style to \a s.
+
+ \sa logicalFontSizeStep()
+*/
+void Q3StyleSheetItem::setLogicalFontSizeStep(int s)
+{
+ d->fontsizestep = s;
+}
+
+
+
+/*!
+ Sets the font size setting of the style to \a s points.
+
+ \sa fontSize(), QFont::pointSize(), QFont::setPointSize()
+*/
+void Q3StyleSheetItem::setFontSize(int s)
+{
+ d->fontsize = s;
+}
+
+/*!
+ Returns the font size setting of the style. This is either a valid
+ point size or Q3StyleSheetItem::Undefined.
+
+ \sa setFontSize(), QFont::pointSize(), QFont::setPointSize()
+*/
+int Q3StyleSheetItem::fontSize() const
+{
+ return d->fontsize;
+}
+
+
+/*!
+ Returns the style's font family setting. This is either a valid
+ font family or an empty string if no family has been set.
+
+ \sa setFontFamily(), QFont::family(), QFont::setFamily()
+*/
+QString Q3StyleSheetItem::fontFamily() const
+{
+ return d->fontfamily;
+}
+
+/*!
+ Sets the font family setting of the style to \a fam.
+
+ \sa fontFamily(), QFont::family(), QFont::setFamily()
+*/
+void Q3StyleSheetItem::setFontFamily(const QString& fam)
+{
+ d->fontfamily = fam;
+}
+
+
+/*!
+ Returns the number of columns for this style.
+
+ \sa setNumberOfColumns(), displayMode(), setDisplayMode()
+
+ */
+int Q3StyleSheetItem::numberOfColumns() const
+{
+ return d->ncolumns;
+}
+
+
+/*!
+ Sets the number of columns for this style to \a ncols. Elements in the style
+ are divided into columns.
+
+ This makes sense only if the style uses a block display mode
+ (see Q3StyleSheetItem::DisplayMode).
+
+ \sa numberOfColumns()
+ */
+void Q3StyleSheetItem::setNumberOfColumns(int ncols)
+{
+ if (ncols > 0)
+ d->ncolumns = ncols;
+}
+
+
+/*!
+ Returns the text color of this style or an invalid color if no
+ color has been set.
+
+ \sa setColor() QColor::isValid()
+*/
+QColor Q3StyleSheetItem::color() const
+{
+ return d->col;
+}
+
+/*!
+ Sets the text color of this style to \a c.
+
+ \sa color()
+*/
+void Q3StyleSheetItem::setColor(const QColor &c)
+{
+ d->col = c;
+}
+
+/*!
+ Returns whether this style is an anchor.
+
+ \sa setAnchor()
+*/
+bool Q3StyleSheetItem::isAnchor() const
+{
+ return d->anchor;
+}
+
+/*!
+ If \a anc is true, sets this style to be an anchor (hypertext
+ link); otherwise sets it to not be an anchor. Elements in this
+ style link to other documents or anchors.
+
+ \sa isAnchor()
+*/
+void Q3StyleSheetItem::setAnchor(bool anc)
+{
+ d->anchor = anc;
+}
+
+
+/*!
+ Returns the whitespace mode.
+
+ \sa setWhiteSpaceMode() WhiteSpaceMode
+*/
+Q3StyleSheetItem::WhiteSpaceMode Q3StyleSheetItem::whiteSpaceMode() const
+{
+ return d->whitespacemode;
+}
+
+/*!
+ Sets the whitespace mode to \a m.
+
+ \sa WhiteSpaceMode
+*/
+void Q3StyleSheetItem::setWhiteSpaceMode(WhiteSpaceMode m)
+{
+ d->whitespacemode = m;
+}
+
+
+/*!
+ Returns the width of margin \a m in pixels.
+
+ The margin, \a m, can be MarginLeft, MarginRight,
+ MarginTop, MarginBottom, or MarginFirstLine
+
+ \sa setMargin() Margin
+*/
+int Q3StyleSheetItem::margin(Margin m) const
+{
+ if (m == MarginAll) {
+ return d->margin[MarginLeft];
+ } else if (m == MarginVertical) {
+ return d->margin[MarginTop];
+ } else if (m == MarginHorizontal) {
+ return d->margin[MarginLeft];
+ } else {
+ return d->margin[m];
+ }
+}
+
+
+/*!
+ Sets the width of margin \a m to \a v pixels.
+
+ The margin, \a m, can be \c MarginLeft, \c MarginRight, \c
+ MarginTop, \c MarginBottom, MarginFirstLine, \c MarginAll,
+ \c MarginVertical or \c MarginHorizontal. The value \a v must
+ be >= 0.
+
+ \sa margin()
+*/
+void Q3StyleSheetItem::setMargin(Margin m, int v)
+{
+ if (m == MarginAll) {
+ d->margin[MarginLeft] = v;
+ d->margin[MarginRight] = v;
+ d->margin[MarginTop] = v;
+ d->margin[MarginBottom] = v;
+ } else if (m == MarginVertical) {
+ d->margin[MarginTop] = v;
+ d->margin[MarginBottom] = v;
+ } else if (m == MarginHorizontal) {
+ d->margin[MarginLeft] = v;
+ d->margin[MarginRight] = v;
+ } else {
+ d->margin[m] = v;
+ }
+}
+
+
+/*!
+ Returns the list style of the style.
+
+ \sa setListStyle() ListStyle
+ */
+Q3StyleSheetItem::ListStyle Q3StyleSheetItem::listStyle() const
+{
+ return d->list;
+}
+
+/*!
+ \enum Q3StyleSheetItem::ListStyle
+
+ This enum type defines how the items in a list are prefixed when
+ displayed.
+
+ \value ListDisc a filled circle (i.e. a bullet)
+ \value ListCircle an unfilled circle
+ \value ListSquare a filled square
+ \value ListDecimal an integer in base 10: \e 1, \e 2, \e 3, ...
+ \value ListLowerAlpha a lowercase letter: \e a, \e b, \e c, ...
+ \value ListUpperAlpha an uppercase letter: \e A, \e B, \e C, ...
+ \omitvalue ListStyleUndefined
+*/
+
+/*!
+ Sets the list style of the style to \a s.
+
+ This is used by nested elements that have a display mode of \c
+ DisplayListItem.
+
+ \sa listStyle() DisplayMode ListStyle
+*/
+void Q3StyleSheetItem::setListStyle(ListStyle s)
+{
+ d->list=s;
+}
+
+
+/*!
+ Returns a space-separated list of names of styles that may contain
+ elements of this style. If nothing has been set, contexts()
+ returns an empty string, which indicates that this style can be
+ nested everywhere.
+
+ \sa setContexts()
+*/
+QString Q3StyleSheetItem::contexts() const
+{
+ return d->contxt;
+}
+
+/*!
+ Sets a space-separated list of names of styles that may contain
+ elements of this style. If \a c is empty, the style can be nested
+ everywhere.
+
+ \sa contexts()
+*/
+void Q3StyleSheetItem::setContexts(const QString& c)
+{
+ d->contxt = QLatin1Char(' ') + c + QLatin1Char(' ');
+}
+
+/*!
+ Returns true if this style can be nested into an element of style
+ \a s; otherwise returns false.
+
+ \sa contexts(), setContexts()
+*/
+bool Q3StyleSheetItem::allowedInContext(const Q3StyleSheetItem* s) const
+{
+ if (d->contxt.isEmpty())
+ return true;
+ return d->contxt.contains(QLatin1Char(' ')+s->name()+QLatin1Char(' '));
+}
+
+
+/*!
+ Returns true if this style has self-nesting enabled; otherwise
+ returns false.
+
+ \sa setSelfNesting()
+*/
+bool Q3StyleSheetItem::selfNesting() const
+{
+ return d->selfnest;
+}
+
+/*!
+ Sets the self-nesting property for this style to \a nesting.
+
+ In order to support "dirty" HTML, paragraphs \c{<p>} and list
+ items \c{<li>} are not self-nesting. This means that starting a
+ new paragraph or list item automatically closes the previous one.
+
+ \sa selfNesting()
+*/
+void Q3StyleSheetItem::setSelfNesting(bool nesting)
+{
+ d->selfnest = nesting;
+}
+
+/*!
+ \internal
+ Sets the linespacing to be at least \a ls pixels.
+
+ For compatibility with previous Qt releases, small values get
+ treated differently: If \a ls is smaller than the default font
+ line spacing in pixels at parse time, the resulting line spacing
+ is the sum of the default line spacing plus \a ls. We recommend
+ not relying on this behavior.
+*/
+
+void Q3StyleSheetItem::setLineSpacing(int ls)
+{
+ d->lineSpacing = ls;
+}
+
+/*!
+ Returns the line spacing.
+*/
+
+int Q3StyleSheetItem::lineSpacing() const
+{
+ return d->lineSpacing;
+}
+
+//************************************************************************
+
+
+
+
+//************************************************************************
+
+
+/*!
+ \class Q3StyleSheet
+ \brief The Q3StyleSheet class is a collection of styles for rich text
+ rendering and a generator of tags.
+
+ \compat
+
+ By creating Q3StyleSheetItem objects for a style sheet you build a
+ definition of a set of tags. This definition will be used by the
+ internal rich text rendering system to parse and display text
+ documents to which the style sheet applies. Rich text is normally
+ visualized in a QTextEdit or a QTextBrowser. However, QLabel,
+ QWhatsThis and QMessageBox also support it, and other classes are
+ likely to follow. With QSimpleRichText it is possible to use the
+ rich text renderer for custom widgets as well.
+
+ The default Q3StyleSheet object has the following style bindings,
+ sorted by structuring bindings, anchors, character style bindings
+ (i.e. inline styles), special elements such as horizontal lines or
+ images, and other tags. In addition, rich text supports simple
+ HTML tables.
+
+ The structuring tags are
+ \table
+ \header \i Structuring tags \i Notes
+ \row \i \c{<qt>}...\c{</qt>}
+ \i A Qt rich text document. It understands the following
+ attributes:
+ \list
+ \i \c title -- The caption of the document. This attribute is
+ easily accessible with QTextEdit::documentTitle().
+ \i \c type -- The type of the document. The default type is \c
+ page. It indicates that the document is displayed in a
+ page of its own. Another style is \c detail, which can be
+ used to explain certain expressions in more detail in a
+ few sentences. For \c detail, QTextBrowser will then keep
+ the current page and display the new document in a small
+ popup similar to QWhatsThis. Note that links will not work
+ in documents with \c{<qt type="detail">...</qt>}.
+ \i \c bgcolor -- The background color, for example \c
+ bgcolor="yellow" or \c bgcolor="#0000FF".
+ \i \c background -- The background pixmap, for example \c
+ background="granite.xpm". The pixmap name will be resolved
+ by a Q3MimeSourceFactory().
+ \i \c text -- The default text color, for example \c text="red".
+ \i \c link -- The link color, for example \c link="green".
+ \endlist
+ \row \i \c{<h1>...</h1>}
+ \i A top-level heading.
+ \row \i \c{<h2>...</h2>}
+ \i A sublevel heading.
+ \row \i \c{<h3>...</h3>}
+ \i A sub-sublevel heading.
+ \row \i \c{<p>...</p>}
+ \i A left-aligned paragraph. Adjust the alignment with the \c
+ align attribute. Possible values are \c left, \c right and
+ \c center.
+ \row \i \c{<center>...}<br>\c{</center>}
+ \i A centered paragraph.
+ \row \i \c{<blockquote>...}<br>\c{</blockquote>}
+ \i An indented paragraph that is useful for quotes.
+ \row \i \c{<ul>...</ul>}
+ \i An unordered list. You can also pass a type argument to
+ define the bullet style. The default is \c type=disc;
+ other types are \c circle and \c square.
+ \row \i \c{<ol>...</ol>}
+ \i An ordered list. You can also pass a type argument to
+ define the enumeration label style. The default is \c
+ type="1"; other types are \c "a" and \c "A".
+ \row \i \c{<li>...</li>}
+ \i A list item. This tag can be used only within the context
+ of \c{<ol>} or \c{<ul>}.
+ \row \i \c{<pre>...</pre>}
+ \i For larger chunks of code. Whitespaces in the contents are
+ preserved. For small bits of code use the inline-style \c
+ code.
+ \endtable
+
+ Anchors and links are done with a single tag:
+ \table
+ \header \i Anchor tags \i Notes
+ \row \i \c{<a>...</a>}
+ \i An anchor or link.
+ \list
+ \i A link is created by using an \c href
+ attribute, for example
+ <br>\c{<a href="target.qml">Link Text</a>}. Links to
+ targets within a document are achieved in the same way
+ as for HTML, e.g.
+ <br>\c{<a href="target.qml#subtitle">Link Text</a>}.
+ \i A target is created by using a \c name
+ attribute, for example
+ <br>\c{<a name="subtitle"><h2>Sub Title</h2></a>}.
+ \endlist
+ \endtable
+
+ The default character style bindings are
+ \table
+ \header \i Style tags \i Notes
+ \row \i \c{<em>...</em>}
+ \i Emphasized. By default this is the same as \c{<i>...</i>}
+ (italic).
+ \row \i \c{<strong>...</strong>}
+ \i Strong. By default this is the same as \c{<b>...</b>}
+ (bold).
+ \row \i \c{<i>...</i>}
+ \i Italic font style.
+ \row \i \c{<b>...</b>}
+ \i Bold font style.
+ \row \i \c{<u>...</u>}
+ \i Underlined font style.
+ \row \i \c{<s>...</s>}
+ \i Strike out font style.
+ \row \i \c{<big>...</big>}
+ \i A larger font size.
+ \row \i \c{<small>...</small>}
+ \i A smaller font size.
+ \row \i \c{<sub>...</sub>}
+ \i Subscripted text
+ \row \i \c{<sup>...</sup>}
+ \i Superscripted text
+ \row \i \c{<code>...</code>}
+ \i Indicates code. By default this is the same as
+ \c{<tt>...</tt>} (typewriter). For larger chunks of code
+ use the block-tag \c{<}\c{pre>}.
+ \row \i \c{<tt>...</tt>}
+ \i Typewriter font style.
+ \row \i \c{<font>...</font>}
+ \i Customizes the font size, family and text color. The tag
+ understands the following attributes:
+ \list
+ \i \c color -- The text color, for example \c color="red" or
+ \c color="#FF0000".
+ \i \c size -- The logical size of the font. Logical sizes 1
+ to 7 are supported. The value may either be absolute
+ (for example, \c size=3) or relative (\c size=-2). In
+ the latter case the sizes are simply added.
+ \i \c face -- The family of the font, for example \c face=times.
+ \endlist
+ \endtable
+
+ Special elements are:
+ \table
+ \header \i Special tags \i Notes
+ \row \i \c{<img>}
+ \i An image. The image name for the mime source factory is
+ given in the source attribute, for example
+ \c{<img src="qt.xpm">} The image tag also understands the
+ attributes \c width and \c height that determine the size
+ of the image. If the pixmap does not fit the specified
+ size it will be scaled automatically (by using
+ QImage::smoothScale()).
+
+ The \c align attribute determines where the image is
+ placed. By default, an image is placed inline just like a
+ normal character. Specify \c left or \c right to place the
+ image at the respective side.
+ \row \i \c{<hr>}
+ \i A horizontal line.
+ \row \i \c{<br>}
+ \i A line break.
+ \row \i \c{<nobr>...</nobr>}
+ \i No break. Prevents word wrap.
+ \endtable
+
+ In addition, rich text supports simple HTML tables. A table
+ consists of one or more rows each of which contains one or more
+ cells. Cells are either data cells or header cells, depending on
+ their content. Cells which span rows and columns are supported.
+
+ \table
+ \header \i Table tags \i Notes
+ \row \i \c{<table>...</table>}
+ \i A table. Tables support the following attributes:
+ \list
+ \i \c bgcolor -- The background color.
+ \i \c width -- The table width. This is either an absolute
+ pixel width or a relative percentage of the table's
+ width, for example \c width=80%.
+ \i \c border -- The width of the table border. The default is
+ 0 (= no border).
+ \i \c cellspacing -- Additional space around the table cells.
+ The default is 2.
+ \i \c cellpadding -- Additional space around the contents of
+ table cells. The default is 1.
+ \endlist
+ \row \i \c{<tr>...</tr>}
+ \i A table row. This is only valid within a \c table. Rows
+ support the following attribute:
+ \list
+ \i \c bgcolor -- The background color.
+ \endlist
+ \row \i \c{<th>...</th>}
+ \i A table header cell. Similar to \c td, but defaults to
+ center alignment and a bold font.
+ \row \i \c{<td>...</td>}
+ \i A table data cell. This is only valid within a \c tr.
+ Cells support the following attributes:
+ \list
+ \i \c bgcolor -- The background color.
+ \i \c width -- The cell width. This is either an absolute
+ pixel width or a relative percentage of table's width,
+ for example \c width=50%.
+ \i \c colspan -- Specifies how many columns this cell spans.
+ The default is 1.
+ \i \c rowspan -- Specifies how many rows this cell spans. The
+ default is 1.
+ \i \c align -- Qt::Alignment; possible values are \c left, \c
+ right, and \c center. The default is \c left.
+ \i \c valign -- Qt::Vertical alignment; possible values are \c
+ top, \c middle, and \c bottom. The default is \c middle.
+ \endlist
+ \endtable
+*/
+
+/*!
+ Creates a style sheet called \a name, with parent \a parent. Like
+ any QObject it will be deleted when its parent is destroyed (if
+ the child still exists).
+
+ By default the style sheet has the tag definitions defined above.
+*/
+Q3StyleSheet::Q3StyleSheet(QObject *parent, const char *name)
+ : QObject(parent)
+{
+ setObjectName(QLatin1String(name));
+ init();
+}
+
+/*!
+ Destroys the style sheet. All styles inserted into the style sheet
+ will be deleted.
+*/
+Q3StyleSheet::~Q3StyleSheet()
+{
+ QHash<QString, Q3StyleSheetItem *>::iterator it = styles.begin();
+ while (it != styles.end()) {
+ delete it.value();
+ ++it;
+ }
+}
+
+/*!
+ \internal
+ Initialized the style sheet to the basic Qt style.
+*/
+void Q3StyleSheet::init()
+{
+ nullstyle = new Q3StyleSheetItem(this, QString::fromLatin1(""));
+
+ Q3StyleSheetItem *style;
+
+ style = new Q3StyleSheetItem(this, QLatin1String("qml")); // compatibility
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("qt"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("a"));
+ style->setAnchor(true);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("em"));
+ style->setFontItalic(true);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("i"));
+ style->setFontItalic(true);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("big"));
+ style->setLogicalFontSizeStep(1);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("large")); // compatibility
+ style->setLogicalFontSizeStep(1);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("small"));
+ style->setLogicalFontSizeStep(-1);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("strong"));
+ style->setFontWeight(QFont::Bold);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("b"));
+ style->setFontWeight(QFont::Bold);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h1"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(6);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 18);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h2"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(5);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 16);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h3"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(4);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 14);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h4"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(3);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h5"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(2);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 12);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 4);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("p"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+ style->setSelfNesting(false);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("center"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setAlignment(Qt::AlignCenter);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("twocolumn"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setNumberOfColumns(2);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("multicol"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ (void) new Q3StyleSheetItem(this, QString::fromLatin1("font"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("ul"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setListStyle(Q3StyleSheetItem::ListDisc);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+ style->setMargin(Q3StyleSheetItem::MarginLeft, 40);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("ol"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setListStyle(Q3StyleSheetItem::ListDecimal);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+ style->setMargin(Q3StyleSheetItem::MarginLeft, 40);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("li"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayListItem);
+ style->setSelfNesting(false);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("code"));
+ style->setFontFamily(QString::fromLatin1("Courier New,courier"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("tt"));
+ style->setFontFamily(QString::fromLatin1("Courier New,courier"));
+
+ new Q3StyleSheetItem(this, QString::fromLatin1("img"));
+ new Q3StyleSheetItem(this, QString::fromLatin1("br"));
+ new Q3StyleSheetItem(this, QString::fromLatin1("hr"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("sub"));
+ style->setVerticalAlignment(Q3StyleSheetItem::VAlignSub);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("sup"));
+ style->setVerticalAlignment(Q3StyleSheetItem::VAlignSuper);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("pre"));
+ style->setFontFamily(QString::fromLatin1("Courier New,courier"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setWhiteSpaceMode(Q3StyleSheetItem::WhiteSpacePre);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("blockquote"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setMargin(Q3StyleSheetItem::MarginHorizontal, 40);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("head"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayNone);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("body"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("div"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock) ;
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("span"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("dl"));
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 8);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("dt"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setContexts(QString::fromLatin1("dl"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("dd"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setMargin(Q3StyleSheetItem::MarginLeft, 30);
+ style->setContexts(QString::fromLatin1("dt dl"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("u"));
+ style->setFontUnderline(true);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("s"));
+ style->setFontStrikeOut(true);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("nobr"));
+ style->setWhiteSpaceMode(Q3StyleSheetItem::WhiteSpaceNoWrap);
+
+ // compatibily with some minor 3.0.x Qt versions that had an
+ // undocumented <wsp> tag. ### Remove 3.1
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("wsp"));
+ style->setWhiteSpaceMode(Q3StyleSheetItem::WhiteSpacePre);
+
+ // tables
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("table"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("tr"));
+ style->setContexts(QString::fromLatin1("table"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("td"));
+ style->setContexts(QString::fromLatin1("tr"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("th"));
+ style->setFontWeight(QFont::Bold);
+ style->setAlignment(Qt::AlignCenter);
+ style->setContexts(QString::fromLatin1("tr"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("html"));
+}
+
+
+
+static Q3StyleSheet* defaultsheet = 0;
+static Q3SingleCleanupHandler<Q3StyleSheet> qt_cleanup_stylesheet;
+
+/*!
+ Returns the application-wide default style sheet. This style sheet
+ is used by rich text rendering classes such as QSimpleRichText,
+ QWhatsThis and QMessageBox to define the rendering style and
+ available tags within rich text documents. It also serves as the
+ initial style sheet for the more complex render widgets, QTextEdit
+ and QTextBrowser.
+
+ \sa setDefaultSheet()
+*/
+Q3StyleSheet* Q3StyleSheet::defaultSheet()
+{
+ if (!defaultsheet) {
+ defaultsheet = new Q3StyleSheet();
+ qt_cleanup_stylesheet.set(&defaultsheet);
+ }
+ return defaultsheet;
+}
+
+/*!
+ Sets the application-wide default style sheet to \a sheet,
+ deleting any style sheet previously set. The ownership is
+ transferred to Q3StyleSheet.
+
+ \sa defaultSheet()
+*/
+void Q3StyleSheet::setDefaultSheet(Q3StyleSheet* sheet)
+{
+ if (defaultsheet != sheet) {
+ if (defaultsheet)
+ qt_cleanup_stylesheet.reset();
+ delete defaultsheet;
+ }
+ defaultsheet = sheet;
+ if (defaultsheet)
+ qt_cleanup_stylesheet.set(&defaultsheet);
+}
+
+/*!\internal
+ Inserts \a style. Any tags generated after this time will be
+ bound to this style. Note that \a style becomes owned by the
+ style sheet and will be deleted when the style sheet is destroyed.
+*/
+void Q3StyleSheet::insert(Q3StyleSheetItem* style)
+{
+ styles.insert(style->name(), style);
+}
+
+
+/*!
+ Returns the style called \a name or 0 if there is no such style.
+*/
+Q3StyleSheetItem* Q3StyleSheet::item(const QString& name)
+{
+ if (name.isNull())
+ return 0;
+ return styles.value(name);
+}
+
+/*!
+ \overload
+
+ Returns the style called \a name or 0 if there is no such style
+ (const version)
+*/
+const Q3StyleSheetItem* Q3StyleSheet::item(const QString& name) const
+{
+ if (name.isNull())
+ return 0;
+ return styles.value(name);
+}
+
+/*! Auxiliary function. Converts the plain text string \a plain to a
+ rich text formatted paragraph while preserving most of its look.
+
+ \a mode defines the whitespace mode. Possible values are \c
+ Q3StyleSheetItem::WhiteSpacePre (no wrapping, all whitespaces
+ preserved) and Q3StyleSheetItem::WhiteSpaceNormal (wrapping,
+ simplified whitespaces).
+
+ \sa escape()
+*/
+QString Q3StyleSheet::convertFromPlainText(const QString& plain, Q3StyleSheetItem::WhiteSpaceMode mode)
+{
+ return Qt::convertFromPlainText(plain, Qt::WhiteSpaceMode(mode));
+}
+
+/*!
+ Auxiliary function. Converts the plain text string \a plain to a
+ rich text formatted string with any HTML meta-characters escaped.
+
+ \sa convertFromPlainText()
+*/
+QString Q3StyleSheet::escape(const QString& plain)
+{
+ return Qt::escape(plain);
+}
+
+// Must doc this enum somewhere, and it is logically related to Q3StyleSheet
+
+/*!
+ Returns true if the string \a text is likely to be rich text;
+ otherwise returns false.
+
+ This function uses a fast and therefore simple heuristic. It
+ mainly checks whether there is something that looks like a tag
+ before the first line break. Although the result may be correct
+ for common cases, there is no guarantee.
+*/
+bool Q3StyleSheet::mightBeRichText(const QString& text)
+{
+ return Qt::mightBeRichText(text);
+}
+
+
+/*!
+ \fn void Q3StyleSheet::error(const QString& msg) const
+
+ This virtual function is called when an error occurs when
+ processing rich text. Reimplement it if you need to catch error
+ messages.
+
+ Errors might occur if some rich text strings contain tags that are
+ not understood by the stylesheet, if some tags are nested
+ incorrectly, or if tags are not closed properly.
+
+ \a msg is the error message.
+*/
+void Q3StyleSheet::error(const QString&) const
+{
+}
+
+
+/*!
+ Scales the font \a font to the appropriate physical point size
+ corresponding to the logical font size \a logicalSize.
+
+ When calling this function, \a font has a point size corresponding
+ to the logical font size 3.
+
+ Logical font sizes range from 1 to 7, with 1 being the smallest.
+
+ \sa Q3StyleSheetItem::logicalFontSize(), Q3StyleSheetItem::logicalFontSizeStep(), QFont::setPointSize()
+ */
+void Q3StyleSheet::scaleFont(QFont& font, int logicalSize) const
+{
+ if (logicalSize < 1)
+ logicalSize = 1;
+ if (logicalSize > 7)
+ logicalSize = 7;
+ int baseSize = font.pointSize();
+ bool pixel = false;
+ if (baseSize == -1) {
+ baseSize = font.pixelSize();
+ pixel = true;
+ }
+ int s;
+ switch (logicalSize) {
+ case 1:
+ s = 7*baseSize/10;
+ break;
+ case 2:
+ s = (8 * baseSize) / 10;
+ break;
+ case 4:
+ s = (12 * baseSize) / 10;
+ break;
+ case 5:
+ s = (15 * baseSize) / 10;
+ break;
+ case 6:
+ s = 2 * baseSize;
+ break;
+ case 7:
+ s = (24 * baseSize) / 10;
+ break;
+ default:
+ s = baseSize;
+ }
+ if (pixel)
+ font.setPixelSize(qMax(1, s));
+ else
+ font.setPointSize(qMax(1, s));
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3stylesheet.h b/src/qt3support/text/q3stylesheet.h
new file mode 100644
index 0000000..0351983
--- /dev/null
+++ b/src/qt3support/text/q3stylesheet.h
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3STYLESHEET_H
+#define Q3STYLESHEET_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qfont.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_RICHTEXT
+
+class Q3StyleSheet;
+class Q3TextDocument;
+template<class Key, class T> class QMap;
+class Q3StyleSheetItemData;
+
+class Q_COMPAT_EXPORT Q3StyleSheetItem
+{
+public:
+ Q3StyleSheetItem(Q3StyleSheet* parent, const QString& name);
+ Q3StyleSheetItem(const Q3StyleSheetItem &);
+ ~Q3StyleSheetItem();
+
+ Q3StyleSheetItem& operator=(const Q3StyleSheetItem& other);
+
+ QString name() const;
+
+ Q3StyleSheet* styleSheet();
+ const Q3StyleSheet* styleSheet() const;
+
+ enum AdditionalStyleValues { Undefined = -1 };
+
+ enum DisplayMode {
+ DisplayBlock,
+ DisplayInline,
+ DisplayListItem,
+ DisplayNone,
+ DisplayModeUndefined = -1
+ };
+
+ DisplayMode displayMode() const;
+ void setDisplayMode(DisplayMode m);
+
+ int alignment() const;
+ void setAlignment(int f);
+
+ enum VerticalAlignment {
+ VAlignBaseline,
+ VAlignSub,
+ VAlignSuper
+ };
+
+ VerticalAlignment verticalAlignment() const;
+ void setVerticalAlignment(VerticalAlignment valign);
+
+ int fontWeight() const;
+ void setFontWeight(int w);
+
+ int logicalFontSize() const;
+ void setLogicalFontSize(int s);
+
+ int logicalFontSizeStep() const;
+ void setLogicalFontSizeStep(int s);
+
+ int fontSize() const;
+ void setFontSize(int s);
+
+ QString fontFamily() const;
+ void setFontFamily(const QString&);
+
+ int numberOfColumns() const;
+ void setNumberOfColumns(int ncols);
+
+ QColor color() const;
+ void setColor(const QColor &);
+
+ bool fontItalic() const;
+ void setFontItalic(bool);
+ bool definesFontItalic() const;
+
+ bool fontUnderline() const;
+ void setFontUnderline(bool);
+ bool definesFontUnderline() const;
+
+ bool fontStrikeOut() const;
+ void setFontStrikeOut(bool);
+ bool definesFontStrikeOut() const;
+
+ bool isAnchor() const;
+ void setAnchor(bool anc);
+
+ enum WhiteSpaceMode {
+ WhiteSpaceNormal,
+ WhiteSpacePre,
+ WhiteSpaceNoWrap,
+ WhiteSpaceModeUndefined = -1
+ };
+ WhiteSpaceMode whiteSpaceMode() const;
+ void setWhiteSpaceMode(WhiteSpaceMode m);
+
+ enum Margin {
+ MarginLeft,
+ MarginRight,
+ MarginTop,
+ MarginBottom,
+ MarginFirstLine,
+ MarginAll,
+ MarginVertical,
+ MarginHorizontal,
+ MarginUndefined = -1
+ };
+
+ int margin(Margin m) const;
+ void setMargin(Margin, int);
+
+ enum ListStyle {
+ ListDisc,
+ ListCircle,
+ ListSquare,
+ ListDecimal,
+ ListLowerAlpha,
+ ListUpperAlpha,
+ ListStyleUndefined = -1
+ };
+
+ ListStyle listStyle() const;
+ void setListStyle(ListStyle);
+
+ QString contexts() const;
+ void setContexts(const QString&);
+ bool allowedInContext(const Q3StyleSheetItem*) const;
+
+ bool selfNesting() const;
+ void setSelfNesting(bool);
+
+ void setLineSpacing(int ls);
+ int lineSpacing() const;
+
+private:
+ void init();
+ Q3StyleSheetItemData* d;
+};
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextCustomItem;
+#endif
+
+class Q_COMPAT_EXPORT Q3StyleSheet : public QObject
+{
+ Q_OBJECT
+public:
+ Q3StyleSheet(QObject *parent=0, const char *name=0);
+ virtual ~Q3StyleSheet();
+
+ static Q3StyleSheet* defaultSheet();
+ static void setDefaultSheet(Q3StyleSheet*);
+
+
+ Q3StyleSheetItem* item(const QString& name);
+ const Q3StyleSheetItem* item(const QString& name) const;
+
+ void insert(Q3StyleSheetItem* item);
+
+ static QString escape(const QString&);
+ static QString convertFromPlainText(const QString&,
+ Q3StyleSheetItem::WhiteSpaceMode mode = Q3StyleSheetItem::WhiteSpacePre);
+ static bool mightBeRichText(const QString&);
+
+ virtual void scaleFont(QFont& font, int logicalSize) const;
+
+ virtual void error(const QString&) const;
+
+private:
+ Q_DISABLE_COPY(Q3StyleSheet)
+
+ void init();
+ QHash<QString, Q3StyleSheetItem *> styles;
+ Q3StyleSheetItem* nullstyle;
+};
+
+#endif // QT_NO_RICHTEXT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3STYLESHEET_H
diff --git a/src/qt3support/text/q3syntaxhighlighter.cpp b/src/qt3support/text/q3syntaxhighlighter.cpp
new file mode 100644
index 0000000..a88a75b
--- /dev/null
+++ b/src/qt3support/text/q3syntaxhighlighter.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3syntaxhighlighter.h"
+#include "q3syntaxhighlighter_p.h"
+
+#ifndef QT_NO_SYNTAXHIGHLIGHTER
+#include "q3textedit.h"
+#include "qtimer.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3SyntaxHighlighter
+ \brief The Q3SyntaxHighlighter class is a base class for
+ implementing Q3TextEdit syntax highlighters.
+
+ \compat
+
+ A syntax highligher automatically highlights parts of the text in
+ a Q3TextEdit. Syntax highlighters are often used when the user is
+ entering text in a specific format (for example, source code) and
+ help the user to read the text and identify syntax errors.
+
+ To provide your own syntax highlighting for Q3TextEdit, you must
+ subclass Q3SyntaxHighlighter and reimplement highlightParagraph().
+
+ When you create an instance of your Q3SyntaxHighlighter subclass,
+ pass it the Q3TextEdit that you want the syntax highlighting to be
+ applied to. After this your highlightParagraph() function will be
+ called automatically whenever necessary. Use your
+ highlightParagraph() function to apply formatting (e.g. setting
+ the font and color) to the text that is passed to it.
+*/
+
+/*!
+ Constructs the Q3SyntaxHighlighter and installs it on \a textEdit.
+ Ownership of the Q3SyntaxHighlighter is transferred to the \a
+ textEdit
+*/
+
+Q3SyntaxHighlighter::Q3SyntaxHighlighter(Q3TextEdit *textEdit)
+ : para(0), edit(textEdit), d(new Q3SyntaxHighlighterPrivate)
+{
+ textEdit->document()->setPreProcessor(new Q3SyntaxHighlighterInternal(this));
+ textEdit->document()->invalidate();
+ QTimer::singleShot(0, textEdit->viewport(), SLOT(update()));
+}
+
+/*!
+ Destructor. Uninstalls this syntax highlighter from the textEdit()
+*/
+
+Q3SyntaxHighlighter::~Q3SyntaxHighlighter()
+{
+ delete d;
+ textEdit()->document()->setPreProcessor(0);
+}
+
+/*!
+ \fn int Q3SyntaxHighlighter::highlightParagraph(const QString &text, int endStateOfLastPara)
+
+ This function is called when necessary by the rich text engine,
+ i.e. on paragraphs which have changed.
+
+ In your reimplementation you should parse the paragraph's \a text
+ and call setFormat() as often as necessary to apply any font and
+ color changes that you require. Your function must return a value
+ which indicates the paragraph's end state: see below.
+
+ Some syntaxes can have constructs that span paragraphs. For
+ example, a C++ syntax highlighter should be able to cope with
+ \c{/}\c{*...*}\c{/} comments that span paragraphs. To deal
+ with these cases it is necessary to know the end state of the
+ previous paragraph (e.g. "in comment").
+
+ If your syntax does not have paragraph spanning constructs, simply
+ ignore the \a endStateOfLastPara parameter and always return 0.
+
+ Whenever highlightParagraph() is called it is passed a value for
+ \a endStateOfLastPara. For the very first paragraph this value is
+ always -2. For any other paragraph the value is the value returned
+ by the most recent highlightParagraph() call that applied to the
+ preceding paragraph.
+
+ The value you return is up to you. We recommend only returning 0
+ (to signify that this paragraph's syntax highlighting does not
+ affect the following paragraph), or a positive integer (to signify
+ that this paragraph has ended in the middle of a paragraph
+ spanning construct).
+
+ To find out which paragraph is highlighted, call
+ currentParagraph().
+
+ For example, if you're writing a simple C++ syntax highlighter,
+ you might designate 1 to signify "in comment". For a paragraph
+ that ended in the middle of a comment you'd return 1, and for
+ other paragraphs you'd return 0. In your parsing code if \a
+ endStateOfLastPara was 1, you would highlight the text as a C++
+ comment until you reached the closing \c{*}\c{/}.
+*/
+
+/*!
+ This function is applied to the syntax highlighter's current
+ paragraph (the text of which is passed to the highlightParagraph()
+ function).
+
+ The specified \a font and \a color are applied to the text from
+ position \a start for \a count characters. (If \a count is 0,
+ nothing is done.)
+*/
+
+void Q3SyntaxHighlighter::setFormat(int start, int count, const QFont &font, const QColor &color)
+{
+ if (!para || count <= 0)
+ return;
+ Q3TextFormat *f = 0;
+ f = para->document()->formatCollection()->format(font, color);
+ para->setFormat(start, count, f);
+ f->removeRef();
+}
+
+/*! \overload */
+
+void Q3SyntaxHighlighter::setFormat(int start, int count, const QColor &color)
+{
+ if (!para || count <= 0)
+ return;
+ Q3TextFormat *f = 0;
+ QFont fnt = textEdit()->QWidget::font();
+ f = para->document()->formatCollection()->format(fnt, color);
+ para->setFormat(start, count, f);
+ f->removeRef();
+}
+
+/*! \overload */
+
+void Q3SyntaxHighlighter::setFormat(int start, int count, const QFont &font)
+{
+ if (!para || count <= 0)
+ return;
+ Q3TextFormat *f = 0;
+ QColor c = textEdit()->viewport()->palette().color(textEdit()->viewport()->foregroundRole());
+ f = para->document()->formatCollection()->format(font, c);
+ para->setFormat(start, count, f);
+ f->removeRef();
+}
+
+/*!
+ \fn Q3TextEdit *Q3SyntaxHighlighter::textEdit() const
+
+ Returns the Q3TextEdit on which this syntax highlighter is
+ installed
+*/
+
+/*! Redoes the highlighting of the whole document.
+*/
+
+void Q3SyntaxHighlighter::rehighlight()
+{
+ Q3TextParagraph *s = edit->document()->firstParagraph();
+ while (s) {
+ s->invalidate(0);
+ s->state = -1;
+ s->needPreProcess = true;
+ s = s->next();
+ }
+ edit->repaintContents();
+}
+
+/*!
+ Returns the id of the paragraph which is highlighted, or -1 of no
+ paragraph is currently highlighted.
+
+ Usually this function is called from within highlightParagraph().
+*/
+
+int Q3SyntaxHighlighter::currentParagraph() const
+{
+ return d->currentParagraph;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/text/q3syntaxhighlighter.h b/src/qt3support/text/q3syntaxhighlighter.h
new file mode 100644
index 0000000..7f95216
--- /dev/null
+++ b/src/qt3support/text/q3syntaxhighlighter.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SYNTAXHIGHLIGHTER_H
+#define Q3SYNTAXHIGHLIGHTER_H
+
+#include <QtGui/qfont.h>
+#include <QtGui/qcolor.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3TextEdit;
+class Q3SyntaxHighlighterInternal;
+class Q3SyntaxHighlighterPrivate;
+class Q3TextParagraph;
+
+class Q_COMPAT_EXPORT Q3SyntaxHighlighter
+{
+ friend class Q3SyntaxHighlighterInternal;
+
+public:
+ Q3SyntaxHighlighter(Q3TextEdit *textEdit);
+ virtual ~Q3SyntaxHighlighter();
+
+ virtual int highlightParagraph(const QString &text, int endStateOfLastPara) = 0;
+
+ void setFormat(int start, int count, const QFont &font, const QColor &color);
+ void setFormat(int start, int count, const QColor &color);
+ void setFormat(int start, int count, const QFont &font);
+ Q3TextEdit *textEdit() const { return edit; }
+
+ void rehighlight();
+
+ int currentParagraph() const;
+
+private:
+ Q3TextParagraph *para;
+ Q3TextEdit *edit;
+ Q3SyntaxHighlighterPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SYNTAXHIGHLIGHTER_H
diff --git a/src/qt3support/text/q3syntaxhighlighter_p.h b/src/qt3support/text/q3syntaxhighlighter_p.h
new file mode 100644
index 0000000..8eb42b0
--- /dev/null
+++ b/src/qt3support/text/q3syntaxhighlighter_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SYNTAXHIGHLIGHTER_P_H
+#define Q3SYNTAXHIGHLIGHTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_SYNTAXHIGHLIGHTER
+#include "q3syntaxhighlighter.h"
+#include "private/q3richtext_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SyntaxHighlighterPrivate
+{
+public:
+ Q3SyntaxHighlighterPrivate() :
+ currentParagraph(-1)
+ {}
+
+ int currentParagraph;
+};
+
+class Q3SyntaxHighlighterInternal : public Q3TextPreProcessor
+{
+public:
+ Q3SyntaxHighlighterInternal(Q3SyntaxHighlighter *h) : highlighter(h) {}
+ void process(Q3TextDocument *doc, Q3TextParagraph *p, int, bool invalidate) {
+ if (p->prev() && p->prev()->endState() == -1)
+ process(doc, p->prev(), 0, false);
+
+ highlighter->para = p;
+ QString text = p->string()->toString();
+ int endState = p->prev() ? p->prev()->endState() : -2;
+ int oldEndState = p->endState();
+ highlighter->d->currentParagraph = p->paragId();
+ p->setEndState(highlighter->highlightParagraph(text, endState));
+ highlighter->d->currentParagraph = -1;
+ highlighter->para = 0;
+
+ p->setFirstPreProcess(false);
+ Q3TextParagraph *op = p;
+ p = p->next();
+ if ((!!oldEndState || !!op->endState()) && oldEndState != op->endState() &&
+ invalidate && p && !p->firstPreProcess() && p->endState() != -1) {
+ while (p) {
+ if (p->endState() == -1)
+ return;
+ p->setEndState(-1);
+ p = p->next();
+ }
+ }
+ }
+ Q3TextFormat *format(int) { return 0; }
+
+private:
+ Q3SyntaxHighlighter *highlighter;
+
+ friend class Q3TextEdit;
+};
+
+#endif // QT_NO_SYNTAXHIGHLIGHTER
+
+QT_END_NAMESPACE
+
+#endif // Q3SYNTAXHIGHLIGHTER_P_H
diff --git a/src/qt3support/text/q3textbrowser.cpp b/src/qt3support/text/q3textbrowser.cpp
new file mode 100644
index 0000000..0a83540
--- /dev/null
+++ b/src/qt3support/text/q3textbrowser.cpp
@@ -0,0 +1,526 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textbrowser.h"
+#ifndef QT_NO_TEXTBROWSER
+#include <private/q3richtext_p.h>
+
+#include "qevent.h"
+#include "qdesktopwidget.h"
+#include "qapplication.h"
+#include "qlayout.h"
+#include "qpainter.h"
+#include "qstack.h"
+#include "stdio.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "qbitmap.h"
+#include "qtimer.h"
+#include "qimage.h"
+#include "q3simplerichtext.h"
+#include "q3dragobject.h"
+#include "qurl.h"
+#include "qcursor.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3TextBrowser
+ \brief The Q3TextBrowser class provides a rich text browser with hypertext navigation.
+
+ \compat
+
+ This class extends Q3TextEdit (in read-only mode), adding some
+ navigation functionality so that users can follow links in
+ hypertext documents. The contents of Q3TextEdit is set with
+ setText(), but Q3TextBrowser has an additional function,
+ setSource(), which makes it possible to set the text to a named
+ document. The name is looked up in the text view's mime source
+ factory. If a document name ends with an anchor (for example, "\c
+ #anchor"), the text browser automatically scrolls to that position
+ (using scrollToAnchor()). When the user clicks on a hyperlink, the
+ browser will call setSource() itself, with the link's \c href
+ value as argument. You can track the current source by connetion
+ to the sourceChanged() signal.
+
+ Q3TextBrowser provides backward() and forward() slots which you can
+ use to implement Back and Forward buttons. The home() slot sets
+ the text to the very first document displayed. The linkClicked()
+ signal is emitted when the user clicks a link.
+
+ By using Q3TextEdit::setMimeSourceFactory() you can provide your
+ own subclass of Q3MimeSourceFactory. This makes it possible to
+ access data from anywhere, for example from a network or from a
+ database. See Q3MimeSourceFactory::data() for details.
+
+ If you intend using the mime factory to read the data directly
+ from the file system, you may have to specify the encoding for the
+ file extension you are using. For example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textbrowser.cpp 0
+ This is to ensure that the factory is able to resolve the document
+ names.
+
+ Q3TextBrowser interprets the tags it processes in accordance with
+ the default style sheet. Change the style sheet with
+ \l{setStyleSheet()}; see QStyleSheet for details.
+
+ If you want to provide your users with editable rich text use
+ Q3TextEdit. If you want a text browser without hypertext navigation
+ use Q3TextEdit, and use Q3TextEdit::setReadOnly() to disable
+ editing. If you just need to display a small piece of rich text
+ use QSimpleRichText or QLabel.
+*/
+
+class Q3TextBrowserData
+{
+public:
+ Q3TextBrowserData():textOrSourceChanged(false) {}
+
+ QStack<QString> stack;
+ QStack<QString> forwardStack;
+ QString home;
+ QString curmain;
+ QString curmark;
+
+ /*flag necessary to give the linkClicked() signal some meaningful
+ semantics when somebody connected to it calls setText() or
+ setSource() */
+ bool textOrSourceChanged;
+};
+
+
+/*!
+ Constructs an empty Q3TextBrowser called \a name, with parent \a
+ parent.
+*/
+Q3TextBrowser::Q3TextBrowser(QWidget *parent, const char *name)
+ : Q3TextEdit(parent, name)
+{
+ setReadOnly(true);
+ d = new Q3TextBrowserData;
+
+ viewport()->setMouseTracking(true);
+}
+
+/*!
+ \internal
+*/
+Q3TextBrowser::~Q3TextBrowser()
+{
+ delete d;
+}
+
+
+/*!
+ \property Q3TextBrowser::source
+ \brief the name of the displayed document.
+
+ This is a an empty string if no document is displayed or if the
+ source is unknown.
+
+ Setting this property uses the mimeSourceFactory() to lookup the
+ named document. It also checks for optional anchors and scrolls
+ the document accordingly.
+
+ If the first tag in the document is \c{<qt type=detail>}, the
+ document is displayed as a popup rather than as new document in
+ the browser window itself. Otherwise, the document is displayed
+ normally in the text browser with the text set to the contents of
+ the named document with setText().
+
+ If you are using the filesystem access capabilities of the mime
+ source factory, you must ensure that the factory knows about the
+ encoding of specified files; otherwise no data will be available.
+ The default factory handles a couple of common file extensions
+ such as \c *.html and \c *.txt with reasonable defaults. See
+ Q3MimeSourceFactory::data() for details.
+*/
+
+QString Q3TextBrowser::source() const
+{
+ if (d->stack.isEmpty())
+ return QString();
+ else
+ return d->stack.top();
+}
+
+/*!
+ Reloads the current set source.
+*/
+
+void Q3TextBrowser::reload()
+{
+ QString s = d->curmain;
+ d->curmain = QLatin1String("");
+ setSource(s);
+}
+
+
+void Q3TextBrowser::setSource(const QString& name)
+{
+#ifndef QT_NO_CURSOR
+ if (isVisible())
+ qApp->setOverrideCursor(Qt::WaitCursor);
+#endif
+ d->textOrSourceChanged = true;
+ QString source = name;
+ QString mark;
+ int hash = name.indexOf(QLatin1Char('#'));
+ if (hash != -1) {
+ source = name.left(hash);
+ mark = name.mid(hash+1);
+ }
+
+ if (source.left(5) == QLatin1String("file:"))
+ source = source.mid(6);
+
+ QString url = mimeSourceFactory()->makeAbsolute(source, context());
+ QString txt;
+ bool dosettext = false;
+
+ if (!source.isEmpty() && url != d->curmain) {
+ const QMimeSource* m =
+ mimeSourceFactory()->data(source, context());
+ if (!m){
+ qWarning("Q3TextBrowser: no mimesource for %s", source.latin1());
+ }
+ else {
+ if (!Q3TextDrag::decode(m, txt)) {
+ qWarning("Q3TextBrowser: cannot decode %s", source.latin1());
+ }
+ }
+ if (isVisible()) {
+ QString firstTag = txt.left(txt.indexOf(QLatin1Char('>')) + 1);
+ if (firstTag.left(3) == QLatin1String("<qt") && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) {
+ popupDetail(txt, QCursor::pos());
+#ifndef QT_NO_CURSOR
+ qApp->restoreOverrideCursor();
+#endif
+ return;
+ }
+ }
+
+ d->curmain = url;
+ dosettext = true;
+ }
+
+ d->curmark = mark;
+
+ if (!mark.isEmpty()) {
+ url += QLatin1Char('#');
+ url += mark;
+ }
+ if (d->home.count() == 0)
+ d->home = url;
+
+ if (d->stack.isEmpty() || d->stack.top() != url)
+ d->stack.push(url);
+
+ int stackCount = (int)d->stack.count();
+ if (d->stack.top() == url)
+ stackCount--;
+ emit backwardAvailable(stackCount > 0);
+ stackCount = (int)d->forwardStack.count();
+ if (d->forwardStack.isEmpty() || d->forwardStack.top() == url)
+ stackCount--;
+ emit forwardAvailable(stackCount > 0);
+
+ if (dosettext)
+ Q3TextEdit::setText(txt, url);
+
+ if (!mark.isEmpty())
+ scrollToAnchor(mark);
+ else
+ setContentsPos(0, 0);
+
+#ifndef QT_NO_CURSOR
+ if (isVisible())
+ qApp->restoreOverrideCursor();
+#endif
+
+ emit sourceChanged(url);
+}
+
+/*!
+ \fn void Q3TextBrowser::backwardAvailable(bool available)
+
+ This signal is emitted when the availability of backward()
+ changes. \a available is false when the user is at home();
+ otherwise it is true.
+*/
+
+/*!
+ \fn void Q3TextBrowser::forwardAvailable(bool available)
+
+ This signal is emitted when the availability of forward() changes.
+ \a available is true after the user navigates backward() and false
+ when the user navigates or goes forward().
+*/
+
+/*!
+ \fn void Q3TextBrowser::sourceChanged(const QString& src)
+
+ This signal is emitted when the mime source has changed, \a src
+ being the new source.
+
+ Source changes happen both programmatically when calling
+ setSource(), forward(), backword() or home() or when the user
+ clicks on links or presses the equivalent key sequences.
+*/
+
+/*! \fn void Q3TextBrowser::highlighted (const QString &link)
+
+ This signal is emitted when the user has selected but not
+ activated a link in the document. \a link is the value of the \c
+ href i.e. the name of the target document.
+*/
+
+/*!
+ \fn void Q3TextBrowser::linkClicked(const QString& link)
+
+ This signal is emitted when the user clicks a link. The \a link is
+ the value of the \c href i.e. the name of the target document.
+
+ The \a link will be the absolute location of the document, based
+ on the value of the anchor's href tag and the current context of
+ the document.
+
+ \sa anchorClicked()
+*/
+
+/*!
+ \fn void Q3TextBrowser::anchorClicked(const QString& name, const QString &link)
+
+ This signal is emitted when the user clicks an anchor. The \a link is
+ the value of the \c href i.e. the name of the target document. The \a name
+ is the name of the anchor.
+
+ \sa linkClicked()
+*/
+
+/*!
+ Changes the document displayed to the previous document in the
+ list of documents built by navigating links. Does nothing if there
+ is no previous document.
+
+ \sa forward(), backwardAvailable()
+*/
+void Q3TextBrowser::backward()
+{
+ if (d->stack.count() <= 1)
+ return;
+ d->forwardStack.push(d->stack.pop());
+ setSource(d->stack.pop());
+ emit forwardAvailable(true);
+}
+
+/*!
+ Changes the document displayed to the next document in the list of
+ documents built by navigating links. Does nothing if there is no
+ next document.
+
+ \sa backward(), forwardAvailable()
+*/
+void Q3TextBrowser::forward()
+{
+ if (d->forwardStack.isEmpty())
+ return;
+ setSource(d->forwardStack.pop());
+ emit forwardAvailable(!d->forwardStack.isEmpty());
+}
+
+/*!
+ Changes the document displayed to be the first document the
+ browser displayed.
+*/
+void Q3TextBrowser::home()
+{
+ if (!d->home.isNull())
+ setSource(d->home);
+}
+
+/*!
+ The event \a e is used to provide the following keyboard shortcuts:
+ \table
+ \header \i Keypress \i Action
+ \row \i Alt+Left Arrow \i \l backward()
+ \row \i Alt+Right Arrow \i \l forward()
+ \row \i Alt+Up Arrow \i \l home()
+ \endtable
+*/
+void Q3TextBrowser::keyPressEvent(QKeyEvent * e)
+{
+ if (e->state() & Qt::AltButton) {
+ switch (e->key()) {
+ case Qt::Key_Right:
+ forward();
+ return;
+ case Qt::Key_Left:
+ backward();
+ return;
+ case Qt::Key_Up:
+ home();
+ return;
+ }
+ }
+ Q3TextEdit::keyPressEvent(e);
+}
+
+class QTextDetailPopup : public QWidget
+{
+public:
+ QTextDetailPopup()
+ : QWidget (0, "automatic QText detail widget", Qt::WType_Popup)
+ {
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ }
+
+protected:
+ void mousePressEvent(QMouseEvent *)
+ {
+ close();
+ }
+};
+
+
+void Q3TextBrowser::popupDetail(const QString& contents, const QPoint& pos)
+{
+
+ const int shadowWidth = 6; // also used as '5' and '6' and even '8' below
+ const int vMargin = 8;
+ const int hMargin = 12;
+
+ QWidget* popup = new QTextDetailPopup;
+ popup->setAttribute(Qt::WA_NoSystemBackground, true);
+
+ Q3SimpleRichText* doc = new Q3SimpleRichText(contents, popup->font());
+ doc->adjustSize();
+ QRect r(0, 0, doc->width(), doc->height());
+
+ int w = r.width() + 2*hMargin;
+ int h = r.height() + 2*vMargin;
+
+ popup->resize(w + shadowWidth, h + shadowWidth);
+
+ // okay, now to find a suitable location
+ //###### we need a global fancy popup positioning somewhere
+ popup->move(pos - popup->rect().center());
+ if (popup->geometry().right() > QApplication::desktop()->width())
+ popup->move(QApplication::desktop()->width() - popup->width(),
+ popup->y());
+ if (popup->geometry().bottom() > QApplication::desktop()->height())
+ popup->move(popup->x(),
+ QApplication::desktop()->height() - popup->height());
+ if (popup->x() < 0)
+ popup->move(0, popup->y());
+ if (popup->y() < 0)
+ popup->move(popup->x(), 0);
+
+
+ popup->show();
+
+ // now for super-clever shadow stuff. super-clever mostly in
+ // how many window system problems it skirts around.
+
+ QPainter p(popup);
+ p.setPen(QApplication::palette().color(QPalette::Active, QPalette::WindowText));
+ p.drawRect(0, 0, w, h);
+ p.setPen(QApplication::palette().color(QPalette::Active, QPalette::Mid));
+ p.setBrush(QColor(255, 255, 240));
+ p.drawRect(1, 1, w-2, h-2);
+ p.setPen(Qt::black);
+
+ doc->draw(&p, hMargin, vMargin, r, popup->palette(), 0);
+ delete doc;
+
+ p.drawPoint(w + 5, 6);
+ p.drawLine(w + 3, 6,
+ w + 5, 8);
+ p.drawLine(w + 1, 6,
+ w + 5, 10);
+ int i;
+ for(i=7; i < h; i += 2)
+ p.drawLine(w, i,
+ w + 5, i + 5);
+ for(i = w - i + h; i > 6; i -= 2)
+ p.drawLine(i, h,
+ i + 5, h + 5);
+ for(; i > 0 ; i -= 2)
+ p.drawLine(6, h + 6 - i,
+ i + 5, h + 5);
+}
+
+/*!
+ \fn void Q3TextBrowser::setText(const QString &txt)
+
+ \overload
+
+ Sets the text to \a txt.
+*/
+
+/*!
+ \reimp
+*/
+
+void Q3TextBrowser::setText(const QString &txt, const QString &context)
+{
+ d->textOrSourceChanged = true;
+ d->curmark = QLatin1String("");
+ d->curmain = QLatin1String("");
+ Q3TextEdit::setText(txt, context);
+}
+
+void Q3TextBrowser::emitHighlighted(const QString &s)
+{
+ emit highlighted(s);
+}
+
+void Q3TextBrowser::emitLinkClicked(const QString &s)
+{
+ d->textOrSourceChanged = false;
+ emit linkClicked(s);
+ if (!d->textOrSourceChanged)
+ setSource(s);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTBROWSER
diff --git a/src/qt3support/text/q3textbrowser.h b/src/qt3support/text/q3textbrowser.h
new file mode 100644
index 0000000..e403dfc
--- /dev/null
+++ b/src/qt3support/text/q3textbrowser.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTBROWSER_H
+#define Q3TEXTBROWSER_H
+
+#include <QtGui/qpixmap.h>
+#include <QtGui/qcolor.h>
+#include <Qt3Support/q3textedit.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_TEXTBROWSER
+
+class Q3TextBrowserData;
+
+class Q_COMPAT_EXPORT Q3TextBrowser : public Q3TextEdit
+{
+ Q_OBJECT
+ Q_PROPERTY(QString source READ source WRITE setSource)
+
+ friend class Q3TextEdit;
+
+public:
+ Q3TextBrowser(QWidget* parent=0, const char* name=0);
+ ~Q3TextBrowser();
+
+ QString source() const;
+
+public Q_SLOTS:
+ virtual void setSource(const QString& name);
+ virtual void backward();
+ virtual void forward();
+ virtual void home();
+ virtual void reload();
+ void setText(const QString &txt) { setText(txt, QString()); }
+ virtual void setText(const QString &txt, const QString &context);
+
+Q_SIGNALS:
+ void backwardAvailable(bool);
+ void forwardAvailable(bool);
+ void sourceChanged(const QString&);
+ void highlighted(const QString&);
+ void linkClicked(const QString&);
+ void anchorClicked(const QString&, const QString&);
+
+protected:
+ void keyPressEvent(QKeyEvent * e);
+
+private:
+ Q_DISABLE_COPY(Q3TextBrowser)
+
+ void popupDetail(const QString& contents, const QPoint& pos);
+ bool linksEnabled() const { return true; }
+ void emitHighlighted(const QString &s);
+ void emitLinkClicked(const QString &s);
+ Q3TextBrowserData *d;
+};
+
+#endif // QT_NO_TEXTBROWSER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTBROWSER_H
diff --git a/src/qt3support/text/q3textedit.cpp b/src/qt3support/text/q3textedit.cpp
new file mode 100644
index 0000000..dd291e2
--- /dev/null
+++ b/src/qt3support/text/q3textedit.cpp
@@ -0,0 +1,7244 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textedit.h"
+
+#ifndef QT_NO_TEXTEDIT
+
+#include <private/q3richtext_p.h>
+#include "qpainter.h"
+#include "qpen.h"
+#include "qbrush.h"
+#include "qpixmap.h"
+#include "qfont.h"
+#include "qcolor.h"
+#include "qstyle.h"
+#include "qsize.h"
+#include "qevent.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "q3listbox.h"
+#include "qclipboard.h"
+#include "qcolordialog.h"
+#include "q3stylesheet.h"
+#include "q3dragobject.h"
+#include "qurl.h"
+#include "qcursor.h"
+#include "qregexp.h"
+#include "q3popupmenu.h"
+#include "qstack.h"
+#include "qmetaobject.h"
+#include "q3textbrowser.h"
+#include "private/q3syntaxhighlighter_p.h"
+#include "qtextformat.h"
+#ifndef QT_NO_IM
+#include <qinputcontext.h>
+#endif
+
+#ifndef QT_NO_ACCEL
+#include <qkeysequence.h>
+#define ACCEL_KEY(k) QLatin1Char('\t') + QString(QKeySequence(Qt::CTRL | Qt::Key_ ## k))
+#else
+#define ACCEL_KEY(k) QLatin1Char('\t' )+ QString::fromLatin1("Ctrl+" #k)
+#endif
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+#define LOGOFFSET(i) d->logOffset + i
+#endif
+
+QT_BEGIN_NAMESPACE
+
+struct QUndoRedoInfoPrivate
+{
+ Q3TextString text;
+};
+
+class Q3TextEditPrivate
+{
+public:
+ Q3TextEditPrivate()
+ :preeditStart(-1),preeditLength(-1),numPreeditSelections(0),ensureCursorVisibleInShowEvent(false),
+ tabChangesFocus(false),
+#ifndef QT_NO_CLIPBOARD
+ clipboard_mode(QClipboard::Clipboard),
+#endif
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ od(0), optimMode(false),
+ maxLogLines(-1),
+ logOffset(0),
+#endif
+ autoFormatting((uint)Q3TextEdit::AutoAll),
+ cursorRepaintMode(false),
+ cursorBlinkActive(false)
+
+ {
+ for (int i=0; i<7; i++)
+ id[i] = 0;
+ }
+ int id[7];
+ int preeditStart;
+ int preeditLength;
+ int numPreeditSelections;
+ uint ensureCursorVisibleInShowEvent : 1;
+ uint tabChangesFocus : 1;
+ QString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized
+ QString pressedName;
+ QString onName;
+#ifndef QT_NO_CLIPBOARD
+ QClipboard::Mode clipboard_mode;
+#endif
+ QTimer *trippleClickTimer;
+ QPoint trippleClickPoint;
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ Q3TextEditOptimPrivate * od;
+ bool optimMode : 1;
+ int maxLogLines;
+ int logOffset;
+#endif
+ Q3TextEdit::AutoFormatting autoFormatting;
+ uint cursorRepaintMode : 1;
+ uint cursorBlinkActive : 1;
+};
+
+#ifndef QT_NO_MIME
+class Q3RichTextDrag : public Q3TextDrag
+{
+public:
+ Q3RichTextDrag(QWidget *dragSource = 0, const char *name = 0);
+
+ void setPlainText(const QString &txt) { setText(txt); }
+ void setRichText(const QString &txt) { richTxt = txt; }
+
+ virtual QByteArray encodedData(const char *mime) const;
+ virtual const char* format(int i) const;
+
+ static bool decode(QMimeSource *e, QString &str, const QString &mimetype,
+ const QString &subtype);
+ static bool canDecode(QMimeSource* e);
+
+private:
+ QString richTxt;
+
+};
+
+Q3RichTextDrag::Q3RichTextDrag(QWidget *dragSource, const char *name)
+ : Q3TextDrag(dragSource, name)
+{
+}
+
+QByteArray Q3RichTextDrag::encodedData(const char *mime) const
+{
+ if (qstrcmp("application/x-qrichtext", mime) == 0) {
+ return richTxt.toUtf8(); // #### perhaps we should use USC2 instead?
+ } else
+ return Q3TextDrag::encodedData(mime);
+}
+
+bool Q3RichTextDrag::decode(QMimeSource *e, QString &str, const QString &mimetype,
+ const QString &subtype)
+{
+ if (mimetype == QLatin1String("application/x-qrichtext")) {
+ // do richtext decode
+ const char *mime;
+ int i;
+ for (i = 0; (mime = e->format(i)); ++i) {
+ if (qstrcmp("application/x-qrichtext", mime) != 0)
+ continue;
+ str = QString::fromUtf8(e->encodedData(mime));
+ return true;
+ }
+ return false;
+ }
+
+ // do a regular text decode
+ QString st = subtype;
+ return Q3TextDrag::decode(e, str, st);
+}
+
+bool Q3RichTextDrag::canDecode(QMimeSource* e)
+{
+ if (e->provides("application/x-qrichtext"))
+ return true;
+ return Q3TextDrag::canDecode(e);
+}
+
+const char* Q3RichTextDrag::format(int i) const
+{
+ if (Q3TextDrag::format(i))
+ return Q3TextDrag::format(i);
+ if (Q3TextDrag::format(i-1))
+ return "application/x-qrichtext";
+ return 0;
+}
+
+#endif
+
+static bool block_set_alignment = false;
+
+/*!
+ \class Q3TextEdit
+ \brief The Q3TextEdit widget provides a powerful single-page rich text editor.
+
+ \compat
+
+ \tableofcontents
+
+ \section1 Introduction and Concepts
+
+ Q3TextEdit is an advanced WYSIWYG viewer/editor supporting rich
+ text formatting using HTML-style tags. It is optimized to handle
+ large documents and to respond quickly to user input.
+
+ Q3TextEdit has four modes of operation:
+ \table
+ \header \i Mode \i Command \i Notes
+ \row \i Plain Text Editor \i setTextFormat(Qt::PlainText)
+ \i Set text with setText(); text() returns plain text. Text
+ attributes (e.g. colors) can be set, but plain text is always
+ returned.
+ \row \i Rich Text Editor \i setTextFormat(Qt::RichText)
+ \i Set text with setText(); text() returns rich text. Rich
+ text editing is fairly limited. You can't set margins or
+ insert images for example (although you can read and
+ correctly display files that have margins set and that
+ include images). This mode is mostly useful for editing small
+ amounts of rich text.
+ \row \i Text Viewer \i setReadOnly(true)
+ \i Set text with setText() or append() (which has no undo
+ history so is faster and uses less memory); text() returns
+ plain or rich text depending on the textFormat(). This mode
+ can correctly display a large subset of HTML tags.
+ \row \i Log Viewer \i setTextFormat(Qt::LogText)
+ \i Append text using append(). The widget is set to be read
+ only and rich text support is disabled although a few HTML
+ tags (for color, bold, italic and underline) may be used.
+ (See \link #logtextmode Qt::LogText mode\endlink for details.)
+ \endtable
+
+ Q3TextEdit can be used as a syntax highlighting editor when used in
+ conjunction with QSyntaxHighlighter.
+
+ We recommend that you always call setTextFormat() to set the mode
+ you want to use. If you use Qt::AutoText then setText() and
+ append() will try to determine whether the text they are given is
+ plain text or rich text. If you use Qt::RichText then setText() and
+ append() will assume that the text they are given is rich text.
+ insert() simply inserts the text it is given.
+
+ Q3TextEdit works on paragraphs and characters. A paragraph is a
+ formatted string which is word-wrapped to fit into the width of
+ the widget. By default when reading plain text, one newline
+ signify a paragraph. A document consists of zero or more
+ paragraphs, indexed from 0. Characters are indexed on a
+ per-paragraph basis, also indexed from 0. The words in the
+ paragraph are aligned in accordance with the paragraph's
+ alignment(). Paragraphs are separated by hard line breaks. Each
+ character within a paragraph has its own attributes, for example,
+ font and color.
+
+ The text edit documentation uses the following concepts:
+ \list
+ \i \e{current format} --
+ this is the format at the current cursor position, \e and it
+ is the format of the selected text if any.
+ \i \e{current paragraph} -- the paragraph which contains the
+ cursor.
+ \endlist
+
+ Q3TextEdit can display images (using Q3MimeSourceFactory), lists and
+ tables. If the text is too large to view within the text edit's
+ viewport, scroll bars will appear. The text edit can load both
+ plain text and HTML files (a subset of HTML 3.2 and 4). The
+ rendering style and the set of valid tags are defined by a
+ styleSheet(). Custom tags can be created and placed in a custom
+ style sheet. Change the style sheet with \l{setStyleSheet()}; see
+ Q3StyleSheet for details. The images identified by image tags are
+ displayed if they can be interpreted using the text edit's
+ \l{Q3MimeSourceFactory}; see setMimeSourceFactory().
+
+ If you want a text browser with more navigation use QTextBrowser.
+ If you just need to display a small piece of rich text use QLabel
+ or QSimpleRichText.
+
+ If you create a new Q3TextEdit, and want to allow the user to edit
+ rich text, call setTextFormat(Qt::RichText) to ensure that the
+ text is treated as rich text. (Rich text uses HTML tags to set
+ text formatting attributes. See Q3StyleSheet for information on the
+ HTML tags that are supported.). If you don't call setTextFormat()
+ explicitly the text edit will guess from the text itself whether
+ it is rich text or plain text. This means that if the text looks
+ like HTML or XML it will probably be interpreted as rich text, so
+ you should call setTextFormat(Qt::PlainText) to preserve such
+ text.
+
+ Note that we do not intend to add a full-featured web browser
+ widget to Qt (because that would easily double Qt's size and only
+ a few applications would benefit from it). The rich
+ text support in Qt is designed to provide a fast, portable and
+ efficient way to add reasonable online help facilities to
+ applications, and to provide a basis for rich text editors.
+
+ \section1 Using Q3TextEdit as a Display Widget
+
+ Q3TextEdit can display a large HTML subset, including tables and
+ images.
+
+ The text is set or replaced using setText() which deletes any
+ existing text and replaces it with the text passed in the
+ setText() call. If you call setText() with legacy HTML (with
+ setTextFormat(Qt::RichText) in force), and then call text(), the text
+ that is returned may have different markup, but will render the
+ same. Text can be inserted with insert(), paste(), pasteSubType()
+ and append(). Text that is appended does not go into the undo
+ history; this makes append() faster and consumes less memory. Text
+ can also be cut(). The entire text is deleted with clear() and the
+ selected text is deleted with removeSelectedText(). Selected
+ (marked) text can also be deleted with del() (which will delete
+ the character to the right of the cursor if no text is selected).
+
+ Loading and saving text is achieved using setText() and text(),
+ for example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 0
+
+ By default the text edit wraps words at whitespace to fit within
+ the text edit widget. The setWordWrap() function is used to
+ specify the kind of word wrap you want, or \c NoWrap if you don't
+ want any wrapping. Call setWordWrap() to set a fixed pixel width
+ \c FixedPixelWidth, or character column (e.g. 80 column) \c
+ FixedColumnWidth with the pixels or columns specified with
+ setWrapColumnOrWidth(). If you use word wrap to the widget's width
+ \c WidgetWidth, you can specify whether to break on whitespace or
+ anywhere with setWrapPolicy().
+
+ The background color is set differently than other widgets, using
+ setPaper(). You specify a brush style which could be a plain color
+ or a complex pixmap.
+
+ Hypertext links are automatically underlined; this can be changed
+ with setLinkUnderline(). The tab stop width is set with
+ setTabStopWidth().
+
+ The zoomIn() and zoomOut() functions can be used to resize the
+ text by increasing (decreasing for zoomOut()) the point size used.
+ Images are not affected by the zoom functions.
+
+ The lines() function returns the number of lines in the text and
+ paragraphs() returns the number of paragraphs. The number of lines
+ within a particular paragraph is returned by linesOfParagraph().
+ The length of the entire text in characters is returned by
+ length().
+
+ You can scroll to an anchor in the text, e.g.
+ \c{<a name="anchor">} with scrollToAnchor(). The find() function
+ can be used to find and select a given string within the text.
+
+ A read-only Q3TextEdit provides the same functionality as the
+ (obsolete) QTextView. (QTextView is still supplied for
+ compatibility with old code.)
+
+ \section2 Read-only key bindings
+
+ When Q3TextEdit is used read-only the key-bindings are limited to
+ navigation, and text may only be selected with the mouse:
+ \table
+ \header \i Keypresses \i Action
+ \row \i Up \i Move one line up
+ \row \i Down \i Move one line down
+ \row \i Left \i Move one character left
+ \row \i Right \i Move one character right
+ \row \i PageUp \i Move one (viewport) page up
+ \row \i PageDown \i Move one (viewport) page down
+ \row \i Home \i Move to the beginning of the text
+ \row \i End \i Move to the end of the text
+ \row \i Shift+Wheel
+ \i Scroll the page horizontally (the Wheel is the mouse wheel)
+ \row \i Ctrl+Wheel \i Zoom the text
+ \endtable
+
+ The text edit may be able to provide some meta-information. For
+ example, the documentTitle() function will return the text from
+ within HTML \c{<title>} tags.
+
+ The text displayed in a text edit has a \e context. The context is
+ a path which the text edit's Q3MimeSourceFactory uses to resolve
+ the locations of files and images. It is passed to the
+ mimeSourceFactory() when quering data. (See Q3TextEdit() and
+ \l{context()}.)
+
+ \target logtextmode
+ \section2 Using Q3TextEdit in Qt::LogText Mode
+
+ Setting the text format to Qt::LogText puts the widget in a special
+ mode which is optimized for very large texts. In this mode editing
+ and rich text support are disabled (the widget is explicitly set
+ to read-only mode). This allows the text to be stored in a
+ different, more memory efficient manner. However, a certain degree
+ of text formatting is supported through the use of formatting
+ tags. A tag is delimited by \c < and \c {>}. The characters \c
+ {<}, \c > and \c & are escaped by using \c {&lt;}, \c {&gt;} and
+ \c {&amp;}. A tag pair consists of a left and a right tag (or
+ open/close tags). Left-tags mark the starting point for
+ formatting, while right-tags mark the ending point. A right-tag
+ always start with a \c / before the tag keyword. For example \c
+ <b> and \c </b> are a tag pair. Tags can be nested, but they
+ have to be closed in the same order as they are opened. For
+ example, \c <b><u></u></b> is valid, while \c
+ <b><u></b></u> will output an error message.
+
+ By using tags it is possible to change the color, bold, italic and
+ underline settings for a piece of text. A color can be specified
+ by using the HTML font tag \c {<font color=colorname>}. The color
+ name can be one of the color names from the X11 color database, or
+ a RGB hex value (e.g \c {#00ff00}). Example of valid color tags:
+ \c {<font color=red>}, \c{<font color="light blue">},\c {<font
+ color="#223344">}. Bold, italic and underline settings can be
+ specified by the tags \c {<b>}, \c <i> and \c {<u>}. Note that a
+ tag does not necessarily have to be closed. A valid example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 1
+
+ Stylesheets can also be used in Qt::LogText mode. To create and use a
+ custom tag, you could do the following:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 2
+ Note that only the color, bold, underline and italic attributes of
+ a Q3StyleSheetItem is used in Qt::LogText mode.
+
+ Note that you can use setMaxLogLines() to limit the number of
+ lines the widget can hold in Qt::LogText mode.
+
+ There are a few things that you need to be aware of when the
+ widget is in this mode:
+ \list
+ \i Functions that deal with rich text formatting and cursor
+ movement will not work or return anything valid.
+ \i Lines are equivalent to paragraphs.
+ \endlist
+
+ \section1 Using Q3TextEdit as an Editor
+
+ All the information about using Q3TextEdit as a display widget also
+ applies here.
+
+ The current format's attributes are set with setItalic(),
+ setBold(), setUnderline(), setFamily() (font family),
+ setPointSize(), setColor() and setCurrentFont(). The current
+ paragraph's alignment is set with setAlignment().
+
+ Use setSelection() to select text. The setSelectionAttributes()
+ function is used to indicate how selected text should be
+ displayed. Use hasSelectedText() to find out if any text is
+ selected. The currently selected text's position is available
+ using getSelection() and the selected text itself is returned by
+ selectedText(). The selection can be copied to the clipboard with
+ copy(), or cut to the clipboard with cut(). It can be deleted with
+ removeSelectedText(). The entire text can be selected (or
+ unselected) using selectAll(). Q3TextEdit supports multiple
+ selections. Most of the selection functions operate on the default
+ selection, selection 0. If the user presses a non-selecting key,
+ e.g. a cursor key without also holding down Shift, all selections
+ are cleared.
+
+ Set and get the position of the cursor with setCursorPosition()
+ and getCursorPosition() respectively. When the cursor is moved,
+ the signals currentFontChanged(), currentColorChanged() and
+ currentAlignmentChanged() are emitted to reflect the font, color
+ and alignment at the new cursor position.
+
+ If the text changes, the textChanged() signal is emitted, and if
+ the user inserts a new line by pressing Return or Enter,
+ returnPressed() is emitted. The isModified() function will return
+ true if the text has been modified.
+
+ Q3TextEdit provides command-based undo and redo. To set the depth
+ of the command history use setUndoDepth() which defaults to 100
+ steps. To undo or redo the last operation call undo() or redo().
+ The signals undoAvailable() and redoAvailable() indicate whether
+ the undo and redo operations can be executed.
+
+ \section2 Editing key bindings
+
+ The list of key-bindings which are implemented for editing:
+ \table
+ \header \i Keypresses \i Action
+ \row \i Backspace \i Delete the character to the left of the cursor
+ \row \i Delete \i Delete the character to the right of the cursor
+ \row \i Ctrl+A \i Move the cursor to the beginning of the line
+ \row \i Ctrl+B \i Move the cursor one character left
+ \row \i Ctrl+C \i Copy the marked text to the clipboard (also
+ Ctrl+Insert under Windows)
+ \row \i Ctrl+D \i Delete the character to the right of the cursor
+ \row \i Ctrl+E \i Move the cursor to the end of the line
+ \row \i Ctrl+F \i Move the cursor one character right
+ \row \i Ctrl+H \i Delete the character to the left of the cursor
+ \row \i Ctrl+K \i Delete to end of line
+ \row \i Ctrl+N \i Move the cursor one line down
+ \row \i Ctrl+P \i Move the cursor one line up
+ \row \i Ctrl+V \i Paste the clipboard text into line edit
+ (also Shift+Insert under Windows)
+ \row \i Ctrl+X \i Cut the marked text, copy to clipboard
+ (also Shift+Delete under Windows)
+ \row \i Ctrl+Z \i Undo the last operation
+ \row \i Ctrl+Y \i Redo the last operation
+ \row \i Left \i Move the cursor one character left
+ \row \i Ctrl+Left \i Move the cursor one word left
+ \row \i Right \i Move the cursor one character right
+ \row \i Ctrl+Right \i Move the cursor one word right
+ \row \i Up \i Move the cursor one line up
+ \row \i Ctrl+Qt::Up \i Move the cursor one word up
+ \row \i DownArrow \i Move the cursor one line down
+ \row \i Ctrl+Down \i Move the cursor one word down
+ \row \i PageUp \i Move the cursor one page up
+ \row \i PageDown \i Move the cursor one page down
+ \row \i Home \i Move the cursor to the beginning of the line
+ \row \i Ctrl+Home \i Move the cursor to the beginning of the text
+ \row \i End \i Move the cursor to the end of the line
+ \row \i Ctrl+End \i Move the cursor to the end of the text
+ \row \i Shift+Wheel \i Scroll the page horizontally
+ (the Wheel is the mouse wheel)
+ \row \i Ctrl+Wheel \i Zoom the text
+ \endtable
+
+ To select (mark) text hold down the Shift key whilst pressing one
+ of the movement keystrokes, for example, \e{Shift+Right}
+ will select the character to the right, and \e{Shift+Ctrl+Right} will select the word to the right, etc.
+
+ By default the text edit widget operates in insert mode so all
+ text that the user enters is inserted into the text edit and any
+ text to the right of the cursor is moved out of the way. The mode
+ can be changed to overwrite, where new text overwrites any text to
+ the right of the cursor, using setOverwriteMode().
+*/
+
+/*!
+ \enum Q3TextEdit::AutoFormattingFlag
+
+ \value AutoNone Do not perform any automatic formatting
+ \value AutoBulletList Only automatically format bulletted lists
+ \value AutoAll Apply all available autoformatting
+*/
+
+
+/*!
+ \enum Q3TextEdit::KeyboardAction
+
+ This enum is used by doKeyboardAction() to specify which action
+ should be executed:
+
+ \value ActionBackspace Delete the character to the left of the
+ cursor.
+
+ \value ActionDelete Delete the character to the right of the
+ cursor.
+
+ \value ActionReturn Split the paragraph at the cursor position.
+
+ \value ActionKill If the cursor is not at the end of the
+ paragraph, delete the text from the cursor position until the end
+ of the paragraph. If the cursor is at the end of the paragraph,
+ delete the hard line break at the end of the paragraph: this will
+ cause this paragraph to be joined with the following paragraph.
+
+ \value ActionWordBackspace Delete the word to the left of the
+ cursor position.
+
+ \value ActionWordDelete Delete the word to the right of the
+ cursor position
+
+*/
+
+/*!
+ \enum Q3TextEdit::VerticalAlignment
+
+ This enum is used to set the vertical alignment of the text.
+
+ \value AlignNormal Normal alignment
+ \value AlignSuperScript Superscript
+ \value AlignSubScript Subscript
+*/
+
+/*!
+ \enum Q3TextEdit::TextInsertionFlags
+
+ \internal
+
+ \value RedoIndentation
+ \value CheckNewLines
+ \value RemoveSelected
+*/
+
+
+/*!
+ \fn void Q3TextEdit::copyAvailable(bool yes)
+
+ This signal is emitted when text is selected or de-selected in the
+ text edit.
+
+ When text is selected this signal will be emitted with \a yes set
+ to true. If no text has been selected or if the selected text is
+ de-selected this signal is emitted with \a yes set to false.
+
+ If \a yes is true then copy() can be used to copy the selection to
+ the clipboard. If \a yes is false then copy() does nothing.
+
+ \sa selectionChanged()
+*/
+
+
+/*!
+ \fn void Q3TextEdit::textChanged()
+
+ This signal is emitted whenever the text in the text edit changes.
+
+ \sa setText() append()
+*/
+
+/*!
+ \fn void Q3TextEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa setSelection() copyAvailable()
+*/
+
+/*! \fn Q3TextDocument *Q3TextEdit::document() const
+
+ \internal
+
+ This function returns the Q3TextDocument which is used by the text
+ edit.
+*/
+
+/*! \fn void Q3TextEdit::setDocument(Q3TextDocument *doc)
+
+ \internal
+
+ This function sets the Q3TextDocument which should be used by the text
+ edit to \a doc. This can be used, for example, if you want to
+ display a document using multiple views. You would create a
+ Q3TextDocument and set it to the text edits which should display it.
+ You would need to connect to the textChanged() and
+ selectionChanged() signals of all the text edits and update them all
+ accordingly (preferably with a slight delay for efficiency reasons).
+*/
+
+/*!
+ \enum Q3TextEdit::CursorAction
+
+ This enum is used by moveCursor() to specify in which direction
+ the cursor should be moved:
+
+ \value MoveBackward Moves the cursor one character backward
+
+ \value MoveWordBackward Moves the cursor one word backward
+
+ \value MoveForward Moves the cursor one character forward
+
+ \value MoveWordForward Moves the cursor one word forward
+
+ \value MoveUp Moves the cursor up one line
+
+ \value MoveDown Moves the cursor down one line
+
+ \value MoveLineStart Moves the cursor to the beginning of the line
+
+ \value MoveLineEnd Moves the cursor to the end of the line
+
+ \value MoveHome Moves the cursor to the beginning of the document
+
+ \value MoveEnd Moves the cursor to the end of the document
+
+ \value MovePgUp Moves the cursor one viewport page up
+
+ \value MovePgDown Moves the cursor one viewport page down
+*/
+
+/*!
+ \property Q3TextEdit::overwriteMode
+ \brief the text edit's overwrite mode
+
+ If false (the default) characters entered by the user are inserted
+ with any characters to the right being moved out of the way. If
+ true, the editor is in overwrite mode, i.e. characters entered by
+ the user overwrite any characters to the right of the cursor
+ position.
+*/
+
+/*!
+ \fn void Q3TextEdit::setCurrentFont(const QFont &f)
+
+ Sets the font of the current format to \a f.
+
+ If the widget is in Qt::LogText mode this function will do
+ nothing. Use setFont() instead.
+
+ \sa currentFont() setPointSize() setFamily()
+*/
+
+/*!
+ \property Q3TextEdit::undoDepth
+ \brief the depth of the undo history
+
+ The maximum number of steps in the undo/redo history. The default
+ is 100.
+
+ \sa undo() redo()
+*/
+
+/*!
+ \fn void Q3TextEdit::undoAvailable(bool yes)
+
+ This signal is emitted when the availability of undo changes. If
+ \a yes is true, then undo() will work until undoAvailable(false)
+ is next emitted.
+
+ \sa undo() undoDepth()
+*/
+
+/*!
+ \fn void Q3TextEdit::modificationChanged(bool m)
+
+ This signal is emitted when the modification status of the
+ document has changed. If \a m is true, the document was modified,
+ otherwise the modification state has been reset to unmodified.
+
+ \sa modified
+*/
+
+/*!
+ \fn void Q3TextEdit::redoAvailable(bool yes)
+
+ This signal is emitted when the availability of redo changes. If
+ \a yes is true, then redo() will work until redoAvailable(false)
+ is next emitted.
+
+ \sa redo() undoDepth()
+*/
+
+/*!
+ \fn void Q3TextEdit::currentFontChanged(const QFont &f)
+
+ This signal is emitted if the font of the current format has
+ changed.
+
+ The new font is \a f.
+
+ \sa setCurrentFont()
+*/
+
+/*!
+ \fn void Q3TextEdit::currentColorChanged(const QColor &c)
+
+ This signal is emitted if the color of the current format has
+ changed.
+
+ The new color is \a c.
+
+ \sa setColor()
+*/
+
+/*!
+ \fn void Q3TextEdit::currentVerticalAlignmentChanged(Q3TextEdit::VerticalAlignment a)
+
+ This signal is emitted if the vertical alignment of the current
+ format has changed.
+
+ The new vertical alignment is \a a.
+*/
+
+/*!
+ \fn void Q3TextEdit::currentAlignmentChanged(int a)
+
+ This signal is emitted if the alignment of the current paragraph
+ has changed.
+
+ The new alignment is \a a.
+
+ \sa setAlignment()
+*/
+
+/*!
+ \fn void Q3TextEdit::cursorPositionChanged(Q3TextCursor *c)
+
+ \internal
+*/
+
+/*!
+ \fn void Q3TextEdit::cursorPositionChanged(int para, int pos)
+
+ \overload
+
+ This signal is emitted if the position of the cursor has changed.
+ \a para contains the paragraph index and \a pos contains the
+ character position within the paragraph.
+
+ \sa setCursorPosition()
+*/
+
+/*!
+ \fn void Q3TextEdit::clicked(int para, int pos)
+
+ This signal is emitted when the mouse is clicked on the paragraph
+ \a para at character position \a pos.
+
+ \sa doubleClicked()
+*/
+
+/*! \fn void Q3TextEdit::doubleClicked(int para, int pos)
+
+ This signal is emitted when the mouse is double-clicked on the
+ paragraph \a para at character position \a pos.
+
+ \sa clicked()
+*/
+
+
+/*!
+ \fn void Q3TextEdit::returnPressed()
+
+ This signal is emitted if the user pressed the Return or the Enter
+ key.
+*/
+
+/*!
+ \fn Q3TextCursor *Q3TextEdit::textCursor() const
+
+ Returns the text edit's text cursor.
+
+ \warning Q3TextCursor is not in the public API, but in special
+ circumstances you might wish to use it.
+*/
+
+/*!
+ Constructs an empty Q3TextEdit called \a name, with parent \a
+ parent.
+*/
+
+Q3TextEdit::Q3TextEdit(QWidget *parent, const char *name)
+ : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
+ doc(new Q3TextDocument(0)), undoRedoInfo(doc)
+{
+ init();
+}
+
+/*!
+ Constructs a Q3TextEdit called \a name, with parent \a parent. The
+ text edit will display the text \a text using context \a context.
+
+ The \a context is a path which the text edit's Q3MimeSourceFactory
+ uses to resolve the locations of files and images. It is passed to
+ the mimeSourceFactory() when quering data.
+
+ For example if the text contains an image tag,
+ \c{<img src="image.png">}, and the context is "path/to/look/in", the
+ Q3MimeSourceFactory will try to load the image from
+ "path/to/look/in/image.png". If the tag was
+ \c{<img src="/image.png">}, the context will not be used (because
+ Q3MimeSourceFactory recognizes that we have used an absolute path)
+ and will try to load "/image.png". The context is applied in exactly
+ the same way to \e hrefs, for example,
+ \c{<a href="target.html">Target</a>}, would resolve to
+ "path/to/look/in/target.html".
+*/
+
+Q3TextEdit::Q3TextEdit(const QString& text, const QString& context,
+ QWidget *parent, const char *name)
+ : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
+ doc(new Q3TextDocument(0)), undoRedoInfo(doc)
+{
+ init();
+ setText(text, context);
+}
+
+/*!
+ Destructor.
+*/
+
+Q3TextEdit::~Q3TextEdit()
+{
+ delete undoRedoInfo.d;
+ undoRedoInfo.d = 0;
+ delete cursor;
+ delete doc;
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ delete d->od;
+#endif
+ delete d;
+}
+
+void Q3TextEdit::init()
+{
+ d = new Q3TextEditPrivate;
+ doc->formatCollection()->setPaintDevice(this);
+ undoEnabled = true;
+ readonly = true;
+ setReadOnly(false);
+ setFrameStyle(LineEditPanel | Sunken);
+ connect(doc, SIGNAL(minimumWidthChanged(int)),
+ this, SLOT(documentWidthChanged(int)));
+
+ mousePressed = false;
+ inDoubleClick = false;
+ modified = false;
+ mightStartDrag = false;
+ onLink.clear();
+ d->onName.clear();
+ overWrite = false;
+ wrapMode = WidgetWidth;
+ wrapWidth = -1;
+ wPolicy = AtWhiteSpace;
+ inDnD = false;
+ doc->setFormatter(new Q3TextFormatterBreakWords);
+ QFont f = Q3ScrollView::font();
+ if (f.kerning())
+ f.setKerning(false);
+ doc->formatCollection()->defaultFormat()->setFont(f);
+ doc->formatCollection()->defaultFormat()->setColor(palette().color(QPalette::Text));
+ currentFormat = doc->formatCollection()->defaultFormat();
+ currentAlignment = Qt::AlignAuto;
+
+ setBackgroundRole(QPalette::Base);
+ viewport()->setBackgroundRole(QPalette::Base);
+
+ viewport()->setAcceptDrops(true);
+ resizeContents(0, doc->lastParagraph() ?
+ (doc->lastParagraph()->paragId() + 1) * doc->formatCollection()->defaultFormat()->height() : 0);
+
+ setAttribute(Qt::WA_KeyCompression, true);
+ viewport()->setMouseTracking(true);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ cursor = new Q3TextCursor(doc);
+
+ formatTimer = new QTimer(this);
+ connect(formatTimer, SIGNAL(timeout()),
+ this, SLOT(formatMore()));
+ lastFormatted = doc->firstParagraph();
+
+ scrollTimer = new QTimer(this);
+ connect(scrollTimer, SIGNAL(timeout()),
+ this, SLOT(autoScrollTimerDone()));
+
+ interval = 0;
+ changeIntervalTimer = new QTimer(this);
+ connect(changeIntervalTimer, SIGNAL(timeout()),
+ this, SLOT(doChangeInterval()));
+
+ cursorVisible = true;
+ blinkTimer = new QTimer(this);
+ connect(blinkTimer, SIGNAL(timeout()),
+ this, SLOT(blinkCursor()));
+
+#ifndef QT_NO_DRAGANDDROP
+ dragStartTimer = new QTimer(this);
+ connect(dragStartTimer, SIGNAL(timeout()),
+ this, SLOT(startDrag()));
+#endif
+
+ d->trippleClickTimer = new QTimer(this);
+
+ formatMore();
+
+ blinkCursorVisible = false;
+
+ viewport()->setFocusProxy(this);
+ viewport()->setFocusPolicy(Qt::WheelFocus);
+ setFocusPolicy(Qt::WheelFocus);
+ setInputMethodEnabled(true);
+ viewport()->installEventFilter(this);
+ connect(this, SIGNAL(horizontalSliderReleased()), this, SLOT(sliderReleased()));
+ connect(this, SIGNAL(verticalSliderReleased()), this, SLOT(sliderReleased()));
+ installEventFilter(this);
+}
+
+void Q3TextEdit::paintDocument(bool drawAll, QPainter *p, int cx, int cy, int cw, int ch)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ Q_ASSERT(!d->optimMode);
+ if (d->optimMode)
+ return;
+#endif
+
+ bool drawCur = blinkCursorVisible && (hasFocus() || viewport()->hasFocus());
+ if ((hasSelectedText() && !style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, 0, this)) ||
+ isReadOnly() || !cursorVisible)
+ drawCur = false;
+ QPalette pal = palette();
+ if (doc->paper())
+ pal.setBrush(QPalette::Base, *doc->paper());
+
+ if (contentsY() < doc->y()) {
+ p->fillRect(contentsX(), contentsY(), visibleWidth(), doc->y(),
+ pal.base());
+ }
+ if (drawAll && doc->width() - contentsX() < cx + cw) {
+ p->fillRect(doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch,
+ pal.base());
+ }
+
+ p->setBrushOrigin(-contentsX(), -contentsY());
+
+ lastFormatted = doc->draw(p, cx, cy, cw, ch, pal, !drawAll, drawCur, cursor);
+
+ if (lastFormatted == doc->lastParagraph())
+ resizeContents(contentsWidth(), doc->height());
+
+ if (contentsHeight() < visibleHeight() && (!doc->lastParagraph() || doc->lastParagraph()->isValid()) && drawAll)
+ p->fillRect(0, contentsHeight(), visibleWidth(),
+ visibleHeight() - contentsHeight(), pal.base());
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimDrawContents(p, cx, cy, cw, ch);
+ return;
+ }
+#endif
+ paintDocument(true, p, cx, cy, cw, ch);
+ int v;
+ p->setPen(palette().color(foregroundRole()));
+ if (document()->isPageBreakEnabled() && (v = document()->flow()->pageSize()) > 0) {
+ int l = int(cy / v) * v;
+ while (l < cy + ch) {
+ p->drawLine(cx, l, cx + cw - 1, l);
+ l += v;
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+
+void Q3TextEdit::drawContents(QPainter *p)
+{
+ if (horizontalScrollBar()->isVisible() &&
+ verticalScrollBar()->isVisible()) {
+ const QRect verticalRect = verticalScrollBar()->geometry();
+ const QRect horizontalRect = horizontalScrollBar()->geometry();
+
+ QRect cornerRect;
+ cornerRect.setTop(verticalRect.bottom());
+ cornerRect.setBottom(horizontalRect.bottom());
+ cornerRect.setLeft(verticalRect.left());
+ cornerRect.setRight(verticalRect.right());
+
+ p->fillRect(cornerRect, palette().background());
+ }
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3TextEdit::event(QEvent *e)
+{
+ if (e->type() == QEvent::AccelOverride && !isReadOnly()) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ switch(ke->state()) {
+ case Qt::NoButton:
+ case Qt::Keypad:
+ case Qt::ShiftButton:
+ if (ke->key() < Qt::Key_Escape) {
+ ke->accept();
+ } else {
+ switch (ke->key()) {
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Delete:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_Backspace:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ break;
+
+ case Qt::ControlButton:
+ case Qt::ControlButton|Qt::ShiftButton:
+ case Qt::ControlButton|Qt::Keypad:
+ case Qt::ControlButton|Qt::ShiftButton|Qt::Keypad:
+ switch (ke->key()) {
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ ke->ignore();
+ break;
+// Those are too frequently used for application functionality
+/* case Qt::Key_A:
+ case Qt::Key_B:
+ case Qt::Key_D:
+ case Qt::Key_E:
+ case Qt::Key_F:
+ case Qt::Key_H:
+ case Qt::Key_I:
+ case Qt::Key_K:
+ case Qt::Key_N:
+ case Qt::Key_P:
+ case Qt::Key_T:
+*/
+ case Qt::Key_C:
+ case Qt::Key_V:
+ case Qt::Key_X:
+ case Qt::Key_Y:
+ case Qt::Key_Z:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+#if defined (Q_WS_WIN)
+ case Qt::Key_Insert:
+ case Qt::Key_Delete:
+#endif
+ ke->accept();
+ default:
+ break;
+ }
+ break;
+
+ default:
+ switch (ke->key()) {
+#if defined (Q_WS_WIN)
+ case Qt::Key_Insert:
+ ke->accept();
+#endif
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ if (e->type() == QEvent::Show) {
+ if (
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ !d->optimMode &&
+#endif
+ d->ensureCursorVisibleInShowEvent ) {
+ ensureCursorVisible();
+ d->ensureCursorVisibleInShowEvent = false;
+ }
+ if (!d->scrollToAnchor.isEmpty()) {
+ scrollToAnchor(d->scrollToAnchor);
+ d->scrollToAnchor.clear();
+ }
+ }
+ return QWidget::event(e);
+}
+
+/*!
+ Processes the key event, \a e. By default key events are used to
+ provide keyboard navigation and text editing.
+*/
+
+void Q3TextEdit::keyPressEvent(QKeyEvent *e)
+{
+ changeIntervalTimer->stop();
+ interval = 10;
+ bool unknownKey = false;
+ if (isReadOnly()) {
+ if (!handleReadOnlyKeyEvent(e))
+ Q3ScrollView::keyPressEvent(e);
+ changeIntervalTimer->start(100, true);
+ return;
+ }
+
+
+ bool selChanged = false;
+ for (int i = 1; i < doc->numSelections(); ++i) // start with 1 as we don't want to remove the Standard-Selection
+ selChanged = doc->removeSelection(i) || selChanged;
+
+ if (selChanged) {
+ cursor->paragraph()->document()->nextDoubleBuffered = true;
+ repaintChanged();
+ }
+
+ bool clearUndoRedoInfo = true;
+
+
+ switch (e->key()) {
+ case Qt::Key_Left:
+ case Qt::Key_Right: {
+ // a bit hacky, but can't change this without introducing new enum values for move and keeping the
+ // correct semantics and movement for BiDi and non BiDi text.
+ CursorAction a;
+ if (cursor->paragraph()->string()->isRightToLeft() == (e->key() == Qt::Key_Right))
+ a = e->state() & Qt::ControlButton ? MoveWordBackward : MoveBackward;
+ else
+ a = e->state() & Qt::ControlButton ? MoveWordForward : MoveForward;
+ moveCursor(a, e->state() & Qt::ShiftButton);
+ break;
+ }
+ case Qt::Key_Up:
+ moveCursor(e->state() & Qt::ControlButton ? MovePgUp : MoveUp, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Down:
+ moveCursor(e->state() & Qt::ControlButton ? MovePgDown : MoveDown, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Home:
+ moveCursor(e->state() & Qt::ControlButton ? MoveHome : MoveLineStart, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_End:
+ moveCursor(e->state() & Qt::ControlButton ? MoveEnd : MoveLineEnd, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Prior:
+ moveCursor(MovePgUp, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Next:
+ moveCursor(MovePgDown, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Return: case Qt::Key_Enter:
+ if (doc->hasSelection(Q3TextDocument::Standard, false))
+ removeSelectedText();
+ if (textFormat() == Qt::RichText && (e->state() & Qt::ControlButton)) {
+ // Ctrl-Enter inserts a line break in rich text mode
+ insert(QString(QChar(QChar::LineSeparator)), true, false);
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ clearUndoRedoInfo = false;
+ doKeyboardAction(ActionReturn);
+ emit returnPressed();
+ }
+ break;
+ case Qt::Key_Delete:
+#if defined (Q_WS_WIN)
+ if (e->state() & Qt::ShiftButton) {
+ cut();
+ break;
+ } else
+#endif
+ if (doc->hasSelection(Q3TextDocument::Standard, true)) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordDelete
+ : ActionDelete);
+ clearUndoRedoInfo = false;
+
+ break;
+ case Qt::Key_Insert:
+ if (e->state() & Qt::ShiftButton)
+ paste();
+#if defined (Q_WS_WIN)
+ else if (e->state() & Qt::ControlButton)
+ copy();
+#endif
+ else
+ setOverwriteMode(!isOverwriteMode());
+ break;
+ case Qt::Key_Backspace:
+#if defined (Q_WS_WIN)
+ if (e->state() & Qt::AltButton) {
+ if (e->state() & Qt::ControlButton) {
+ break;
+ } else if (e->state() & Qt::ShiftButton) {
+ redo();
+ break;
+ } else {
+ undo();
+ break;
+ }
+ } else
+#endif
+ if (doc->hasSelection(Q3TextDocument::Standard, true)) {
+ removeSelectedText();
+ break;
+ }
+
+ doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordBackspace
+ : ActionBackspace);
+ clearUndoRedoInfo = false;
+ break;
+ case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Qt::Key_F18: // Paste key on Sun keyboards
+ paste();
+ break;
+ case Qt::Key_F20: // Cut key on Sun keyboards
+ cut();
+ break;
+ case Qt::Key_Direction_L:
+ if (doc->textFormat() == Qt::PlainText) {
+ // change the whole doc
+ Q3TextParagraph *p = doc->firstParagraph();
+ while (p) {
+ p->setDirection(QChar::DirL);
+ p->setAlignment(Qt::AlignLeft);
+ p->invalidate(0);
+ p = p->next();
+ }
+ } else {
+ if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirL)
+ return;
+ cursor->paragraph()->setDirection(QChar::DirL);
+ if (cursor->paragraph()->length() <= 1&&
+ ((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0))
+ setAlignment(Qt::AlignLeft);
+ }
+ repaintChanged();
+ break;
+ case Qt::Key_Direction_R:
+ if (doc->textFormat() == Qt::PlainText) {
+ // change the whole doc
+ Q3TextParagraph *p = doc->firstParagraph();
+ while (p) {
+ p->setDirection(QChar::DirR);
+ p->setAlignment(Qt::AlignRight);
+ p->invalidate(0);
+ p = p->next();
+ }
+ } else {
+ if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirR)
+ return;
+ cursor->paragraph()->setDirection(QChar::DirR);
+ if (cursor->paragraph()->length() <= 1&&
+ ((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0))
+ setAlignment(Qt::AlignRight);
+ }
+ repaintChanged();
+ break;
+ default: {
+ unsigned char ascii = e->text().length() ? e->text().unicode()->latin1() : 0;
+ if (e->text().length() &&
+ ((!(e->state() & Qt::ControlButton) &&
+#ifndef Q_OS_MAC
+ !(e->state() & Qt::AltButton) &&
+#endif
+ !(e->state() & Qt::MetaButton)) ||
+ (((e->state() & (Qt::ControlButton | Qt::AltButton))) == (Qt::ControlButton|Qt::AltButton))) &&
+ (!ascii || ascii >= 32 || e->text() == QString(QLatin1Char('\t')))) {
+ clearUndoRedoInfo = false;
+ if (e->key() == Qt::Key_Tab) {
+ if (d->tabChangesFocus) {
+ e->ignore();
+ break;
+ }
+ if (textFormat() == Qt::RichText && cursor->index() == 0
+ && (cursor->paragraph()->isListItem() || cursor->paragraph()->listDepth())) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.eid = undoRedoInfo.id;
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+ cursor->paragraph()->setListDepth(cursor->paragraph()->listDepth() +1);
+ clearUndoRedo();
+ drawCursor(false);
+ repaintChanged();
+ drawCursor(true);
+ break;
+ }
+ } else if (e->key() == Qt::Key_BackTab) {
+ if (d->tabChangesFocus) {
+ e->ignore();
+ break;
+ }
+ }
+
+ if ((autoFormatting() & AutoBulletList) &&
+ textFormat() == Qt::RichText && cursor->index() == 0
+ && !cursor->paragraph()->isListItem()
+ && (e->text()[0] == QLatin1Char('-') || e->text()[0] == QLatin1Char('*'))) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.eid = undoRedoInfo.id;
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+ setParagType(Q3StyleSheetItem::DisplayListItem, Q3StyleSheetItem::ListDisc);
+ clearUndoRedo();
+ drawCursor(false);
+ repaintChanged();
+ drawCursor(true);
+ break;
+ }
+ if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(Q3TextDocument::Standard)) {
+ doKeyboardAction(ActionDelete);
+ clearUndoRedoInfo = false;
+ }
+ QString t = e->text();
+ insert(t, true, false);
+ break;
+ } else if (e->state() & Qt::ControlButton) {
+ switch (e->key()) {
+ case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Qt::Key_V:
+ paste();
+ break;
+ case Qt::Key_X:
+ cut();
+ break;
+ case Qt::Key_I: case Qt::Key_T: case Qt::Key_Tab:
+ if (!d->tabChangesFocus)
+ indent();
+ break;
+ case Qt::Key_A:
+#if defined(Q_WS_X11)
+ moveCursor(MoveLineStart, e->state() & Qt::ShiftButton);
+#else
+ selectAll(true);
+#endif
+ break;
+ case Qt::Key_B:
+ moveCursor(MoveBackward, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_F:
+ moveCursor(MoveForward, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_D:
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction(ActionDelete);
+ clearUndoRedoInfo = false;
+ break;
+ case Qt::Key_H:
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ break;
+ }
+ if (!cursor->paragraph()->prev() &&
+ cursor->atParagStart())
+ break;
+
+ doKeyboardAction(ActionBackspace);
+ clearUndoRedoInfo = false;
+ break;
+ case Qt::Key_E:
+ moveCursor(MoveLineEnd, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_N:
+ moveCursor(MoveDown, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_P:
+ moveCursor(MoveUp, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Z:
+ if(e->state() & Qt::ShiftButton)
+ redo();
+ else
+ undo();
+ break;
+ case Qt::Key_Y:
+ redo();
+ break;
+ case Qt::Key_K:
+ doKeyboardAction(ActionKill);
+ break;
+#if defined(Q_WS_WIN)
+ case Qt::Key_Insert:
+ copy();
+ break;
+ case Qt::Key_Delete:
+ del();
+ break;
+#endif
+ default:
+ unknownKey = false;
+ break;
+ }
+ } else {
+ unknownKey = true;
+ }
+ }
+ }
+
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ if (clearUndoRedoInfo)
+ clearUndoRedo();
+ changeIntervalTimer->start(100, true);
+ if (unknownKey)
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+void Q3TextEdit::inputMethodEvent(QInputMethodEvent *e)
+{
+ if (isReadOnly()) {
+ e->ignore();
+ return;
+ }
+
+ if (hasSelectedText())
+ removeSelectedText();
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::IME;
+
+ bool oldupdate = updatesEnabled();
+ if (oldupdate)
+ setUpdatesEnabled(false);
+ bool sigs_blocked = signalsBlocked();
+ blockSignals(true);
+ const int preeditSelectionBase = 31900;
+ for (int i = 0; i < d->numPreeditSelections; ++i)
+ doc->removeSelection(preeditSelectionBase + i);
+ d->numPreeditSelections = 0;
+
+ if (d->preeditLength > 0 && cursor->paragraph()) {
+ cursor->setIndex(d->preeditStart);
+ cursor->paragraph()->remove(d->preeditStart, d->preeditLength);
+ d->preeditStart = d->preeditLength = -1;
+ }
+
+ if (!e->commitString().isEmpty() || e->replacementLength()) {
+ int c = cursor->index(); // cursor position after insertion of commit string
+ if (e->replacementStart() <= 0)
+ c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength());
+ cursor->setIndex(cursor->index() + e->replacementStart());
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ cursor->setIndex(cursor->index() + e->replacementLength());
+ doc->setSelectionEnd(Q3TextDocument::Standard, *cursor);
+ removeSelectedText();
+ if (undoRedoInfo.type == UndoRedoInfo::IME)
+ undoRedoInfo.type = UndoRedoInfo::Invalid;
+ insert(e->commitString());
+ undoRedoInfo.type = UndoRedoInfo::IME;
+ cursor->setIndex(c);
+ }
+
+ if (!e->preeditString().isEmpty()) {
+ d->preeditStart = cursor->index();
+ d->preeditLength = e->preeditString().length();
+ insert(e->preeditString());
+ cursor->setIndex(d->preeditStart);
+
+ Q3TextCursor c = *cursor;
+ for (int i = 0; i < e->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = e->attributes().at(i);
+ if (a.type == QInputMethodEvent::Cursor)
+ cursor->setIndex(cursor->index() + a.start);
+ else if (a.type != QInputMethodEvent::TextFormat)
+ continue;
+ QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
+ if (f.isValid()) {
+ Q3TextCursor c2 = c;
+ c2.setIndex(c.index() + a.start);
+ doc->setSelectionStart(preeditSelectionBase + d->numPreeditSelections, c2);
+ c2.setIndex(c.index() + a.start + a.length);
+ doc->setSelectionEnd(preeditSelectionBase + d->numPreeditSelections, c2);
+
+ QColor c = f.hasProperty(QTextFormat::BackgroundBrush) ? f.background().color() : QColor();
+ doc->setSelectionColor(preeditSelectionBase + d->numPreeditSelections, c);
+ c = f.hasProperty(QTextFormat::ForegroundBrush) ? f.foreground().color() : QColor();
+ doc->setSelectionTextColor(preeditSelectionBase + d->numPreeditSelections, c);
+ if (f.fontUnderline()) {
+ Q3TextParagraph *par = cursor->paragraph();
+ Q3TextFormat f(*par->string()->at(d->preeditStart).format());
+ f.setUnderline(true);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ par->setFormat(d->preeditStart + a.start, a.length, f2);
+ }
+ ++d->numPreeditSelections;
+ }
+ }
+ } else {
+ undoRedoInfo.type = UndoRedoInfo::Invalid;
+ }
+ blockSignals(sigs_blocked);
+ if (oldupdate)
+ setUpdatesEnabled(true);
+ if (!e->commitString().isEmpty())
+ emit textChanged();
+ repaintChanged();
+}
+
+
+static bool qtextedit_ignore_readonly = false;
+
+/*!
+ Executes keyboard action \a action. This is normally called by a
+ key event handler.
+*/
+
+void Q3TextEdit::doKeyboardAction(Q3TextEdit::KeyboardAction action)
+{
+ if (isReadOnly() && !qtextedit_ignore_readonly)
+ return;
+
+ if (cursor->nestedDepth() != 0)
+ return;
+
+ lastFormatted = cursor->paragraph();
+ drawCursor(false);
+ bool doUpdateCurrentFormat = true;
+
+ switch (action) {
+ case ActionWordDelete:
+ case ActionDelete:
+ if (action == ActionDelete && !cursor->atParagEnd()) {
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::Delete);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ int idx = cursor->index();
+ do {
+ undoRedoInfo.d->text.insert(undoRedoInfo.d->text.length(), cursor->paragraph()->at(idx++), true);
+ } while (!cursor->paragraph()->string()->validCursorPosition(idx));
+ }
+ cursor->remove();
+ } else {
+ clearUndoRedo();
+ doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
+ if (action == ActionWordDelete && !cursor->atParagEnd()) {
+ cursor->gotoNextWord();
+ } else {
+ cursor->gotoNextLetter();
+ }
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ removeSelectedText(Q3TextDocument::Temp);
+ }
+ break;
+ case ActionWordBackspace:
+ case ActionBackspace:
+ if (textFormat() == Qt::RichText
+ && (cursor->paragraph()->isListItem()
+ || cursor->paragraph()->listDepth())
+ && cursor->index() == 0) {
+ if (undoEnabled) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.eid = undoRedoInfo.id;
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+ }
+ int ldepth = cursor->paragraph()->listDepth();
+ if (cursor->paragraph()->isListItem() && ldepth == 1) {
+ cursor->paragraph()->setListItem(false);
+ } else if (qMax(ldepth, 1) == 1) {
+ cursor->paragraph()->setListItem(false);
+ cursor->paragraph()->setListDepth(0);
+ } else {
+ cursor->paragraph()->setListDepth(ldepth - 1);
+ }
+ clearUndoRedo();
+ lastFormatted = cursor->paragraph();
+ repaintChanged();
+ drawCursor(true);
+ return;
+ }
+
+ if (action == ActionBackspace && !cursor->atParagStart()) {
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::Delete);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ undoRedoInfo.d->text.insert(0, cursor->paragraph()->at(cursor->index()-1), true);
+ undoRedoInfo.index = cursor->index()-1;
+ }
+ cursor->removePreviousChar();
+ lastFormatted = cursor->paragraph();
+ } else if (cursor->paragraph()->prev()
+ || (action == ActionWordBackspace
+ && !cursor->atParagStart())) {
+ clearUndoRedo();
+ doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
+ if (action == ActionWordBackspace && !cursor->atParagStart()) {
+ cursor->gotoPreviousWord();
+ } else {
+ cursor->gotoPreviousLetter();
+ }
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ removeSelectedText(Q3TextDocument::Temp);
+ }
+ break;
+ case ActionReturn:
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::Return);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ undoRedoInfo.d->text += QString(QLatin1Char('\n'));
+ }
+ cursor->splitAndInsertEmptyParagraph();
+ if (cursor->paragraph()->prev()) {
+ lastFormatted = cursor->paragraph()->prev();
+ lastFormatted->invalidate(0);
+ }
+ doUpdateCurrentFormat = false;
+ break;
+ case ActionKill:
+ clearUndoRedo();
+ doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
+ if (cursor->atParagEnd())
+ cursor->gotoNextLetter();
+ else
+ cursor->setIndex(cursor->paragraph()->length() - 1);
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ removeSelectedText(Q3TextDocument::Temp);
+ break;
+ }
+
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+ if (doUpdateCurrentFormat)
+ updateCurrentFormat();
+ setModified();
+ emit textChanged();
+}
+
+void Q3TextEdit::readFormats(Q3TextCursor &c1, Q3TextCursor &c2, Q3TextString &text, bool fillStyles)
+{
+#ifndef QT_NO_DATASTREAM
+ QDataStream styleStream(&undoRedoInfo.styleInformation, IO_WriteOnly);
+#endif
+ c2.restoreState();
+ c1.restoreState();
+ int lastIndex = text.length();
+ if (c1.paragraph() == c2.paragraph()) {
+ for (int i = c1.index(); i < c2.index(); ++i)
+ text.insert(lastIndex + i - c1.index(), c1.paragraph()->at(i), true);
+#ifndef QT_NO_DATASTREAM
+ if (fillStyles) {
+ styleStream << (int) 1;
+ c1.paragraph()->writeStyleInformation(styleStream);
+ }
+#endif
+ } else {
+ int i;
+ for (i = c1.index(); i < c1.paragraph()->length()-1; ++i)
+ text.insert(lastIndex++, c1.paragraph()->at(i), true);
+ int num = 2; // start and end, being different
+ text += QString(QLatin1Char('\n')); lastIndex++;
+
+ if (c1.paragraph()->next() != c2.paragraph()) {
+ num += text.appendParagraphs(c1.paragraph()->next(), c2.paragraph());
+ lastIndex = text.length();
+ }
+
+ for (i = 0; i < c2.index(); ++i)
+ text.insert(i + lastIndex, c2.paragraph()->at(i), true);
+#ifndef QT_NO_DATASTREAM
+ if (fillStyles) {
+ styleStream << num;
+ for (Q3TextParagraph *p = c1.paragraph(); --num >= 0; p = p->next())
+ p->writeStyleInformation(styleStream);
+ }
+#endif
+ }
+}
+
+/*!
+ Removes the selection \a selNum (by default 0). This does not
+ remove the selected text.
+
+ \sa removeSelectedText()
+*/
+
+void Q3TextEdit::removeSelection(int selNum)
+{
+ doc->removeSelection(selNum);
+ repaintChanged();
+}
+
+/*!
+ Deletes the text of selection \a selNum (by default, the default
+ selection, 0). If there is no selected text nothing happens.
+
+ \sa selectedText removeSelection()
+*/
+
+void Q3TextEdit::removeSelectedText(int selNum)
+{
+ Q3TextCursor c1 = doc->selectionStartCursor(selNum);
+ c1.restoreState();
+ Q3TextCursor c2 = doc->selectionEndCursor(selNum);
+ c2.restoreState();
+
+ // ### no support for editing tables yet, plus security for broken selections
+ if (c1.nestedDepth() || c2.nestedDepth())
+ return;
+
+ for (int i = 0; i < (int)doc->numSelections(); ++i) {
+ if (i == selNum)
+ continue;
+ doc->removeSelection(i);
+ }
+
+ drawCursor(false);
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::RemoveSelected);
+ if (!undoRedoInfo.valid()) {
+ doc->selectionStart(selNum, undoRedoInfo.id, undoRedoInfo.index);
+ undoRedoInfo.d->text.clear();
+ }
+ readFormats(c1, c2, undoRedoInfo.d->text, true);
+ }
+
+ doc->removeSelectedText(selNum, cursor);
+ if (cursor->isValid()) {
+ lastFormatted = 0; // make sync a noop
+ ensureCursorVisible();
+ lastFormatted = cursor->paragraph();
+ formatMore();
+ repaintContents();
+ ensureCursorVisible();
+ drawCursor(true);
+ clearUndoRedo();
+#if defined(Q_WS_WIN)
+ // there seems to be a problem with repainting or erasing the area
+ // of the scrollview which is not the contents on windows
+ if (contentsHeight() < visibleHeight())
+ viewport()->repaint(0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight());
+#endif
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ } else {
+ lastFormatted = doc->firstParagraph();
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ drawCursor(true);
+ repaintContents();
+ }
+ setModified();
+ emit textChanged();
+ emit selectionChanged();
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+}
+
+/*!
+ Moves the text cursor according to \a action. This is normally
+ used by some key event handler. \a select specifies whether the
+ text between the current cursor position and the new position
+ should be selected.
+*/
+
+void Q3TextEdit::moveCursor(Q3TextEdit::CursorAction action, bool select)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+#ifdef Q_WS_MAC
+ Q3TextCursor c1 = *cursor;
+ Q3TextCursor c2;
+#endif
+ drawCursor(false);
+ if (select) {
+ if (!doc->hasSelection(Q3TextDocument::Standard))
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ moveCursor(action);
+#ifdef Q_WS_MAC
+ c2 = *cursor;
+ if (c1 == c2)
+ if (action == MoveDown || action == MovePgDown)
+ moveCursor(MoveEnd);
+ else if (action == MoveUp || action == MovePgUp)
+ moveCursor(MoveHome);
+#endif
+ if (doc->setSelectionEnd(Q3TextDocument::Standard, *cursor)) {
+ cursor->paragraph()->document()->nextDoubleBuffered = true;
+ repaintChanged();
+ } else {
+ drawCursor(true);
+ }
+ ensureCursorVisible();
+ emit selectionChanged();
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ } else {
+#ifdef Q_WS_MAC
+ Q3TextCursor cStart = doc->selectionStartCursor(Q3TextDocument::Standard);
+ Q3TextCursor cEnd = doc->selectionEndCursor(Q3TextDocument::Standard);
+ bool redraw = doc->removeSelection(Q3TextDocument::Standard);
+ if (redraw && action == MoveDown)
+ *cursor = cEnd;
+ else if (redraw && action == MoveUp)
+ *cursor = cStart;
+ if (redraw && action == MoveForward)
+ *cursor = cEnd;
+ else if (redraw && action == MoveBackward)
+ *cursor = cStart;
+ else
+ moveCursor(action);
+ c2 = *cursor;
+ if (c1 == c2)
+ if (action == MoveDown)
+ moveCursor(MoveEnd);
+ else if (action == MoveUp)
+ moveCursor(MoveHome);
+#else
+ bool redraw = doc->removeSelection(Q3TextDocument::Standard);
+ moveCursor(action);
+#endif
+ if (!redraw) {
+ ensureCursorVisible();
+ drawCursor(true);
+ } else {
+ cursor->paragraph()->document()->nextDoubleBuffered = true;
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ if (redraw) {
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ emit selectionChanged();
+ }
+ }
+
+ drawCursor(true);
+ updateCurrentFormat();
+}
+
+/*!
+ \overload
+*/
+
+void Q3TextEdit::moveCursor(Q3TextEdit::CursorAction action)
+{
+ resetInputContext();
+ switch (action) {
+ case MoveBackward:
+ cursor->gotoPreviousLetter();
+ break;
+ case MoveWordBackward:
+ cursor->gotoPreviousWord();
+ break;
+ case MoveForward:
+ cursor->gotoNextLetter();
+ break;
+ case MoveWordForward:
+ cursor->gotoNextWord();
+ break;
+ case MoveUp:
+ cursor->gotoUp();
+ break;
+ case MovePgUp:
+ cursor->gotoPageUp(visibleHeight());
+ break;
+ case MoveDown:
+ cursor->gotoDown();
+ break;
+ case MovePgDown:
+ cursor->gotoPageDown(visibleHeight());
+ break;
+ case MoveLineStart:
+ cursor->gotoLineStart();
+ break;
+ case MoveHome:
+ cursor->gotoHome();
+ break;
+ case MoveLineEnd:
+ cursor->gotoLineEnd();
+ break;
+ case MoveEnd:
+ ensureFormatted(doc->lastParagraph());
+ cursor->gotoEnd();
+ break;
+ }
+ updateCurrentFormat();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::resizeEvent(QResizeEvent *e)
+{
+ Q3ScrollView::resizeEvent(e);
+ if (doc->visibleWidth() == 0)
+ doResize();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::viewportResizeEvent(QResizeEvent *e)
+{
+ Q3ScrollView::viewportResizeEvent(e);
+ if (e->oldSize().width() != e->size().width()) {
+ bool stayAtBottom = e->oldSize().height() != e->size().height() &&
+ contentsY() > 0 && contentsY() >= doc->height() - e->oldSize().height();
+ doResize();
+ if (stayAtBottom)
+ scrollToBottom();
+ }
+}
+
+/*!
+ Ensures that the cursor is visible by scrolling the text edit if
+ necessary.
+
+ \sa setCursorPosition()
+*/
+
+void Q3TextEdit::ensureCursorVisible()
+{
+ // Not visible or the user is dragging the window, so don't position to caret yet
+ if (!updatesEnabled() || !isVisible() || isHorizontalSliderPressed() || isVerticalSliderPressed()) {
+ d->ensureCursorVisibleInShowEvent = true;
+ return;
+ }
+ sync();
+ Q3TextStringChar *chr = cursor->paragraph()->at(cursor->index());
+ int h = cursor->paragraph()->lineHeightOfChar(cursor->index());
+ int x = cursor->paragraph()->rect().x() + chr->x + cursor->offsetX();
+ int y = 0; int dummy;
+ cursor->paragraph()->lineHeightOfChar(cursor->index(), &dummy, &y);
+ y += cursor->paragraph()->rect().y() + cursor->offsetY();
+ int w = 1;
+ ensureVisible(x, y + h / 2, w, h / 2 + 2);
+}
+
+/*!
+ \internal
+*/
+void Q3TextEdit::sliderReleased()
+{
+ if (d->ensureCursorVisibleInShowEvent && isVisible()) {
+ d->ensureCursorVisibleInShowEvent = false;
+ ensureCursorVisible();
+ }
+}
+
+/*!
+ \internal
+
+ If \a visible is true, the cursor is shown; otherwise it is
+ hidden.
+*/
+void Q3TextEdit::drawCursor(bool visible)
+{
+ d->cursorRepaintMode = true;
+ blinkCursorVisible = visible;
+ QRect r(cursor->topParagraph()->rect());
+ if (!cursor->nestedDepth()) {
+ int h = cursor->paragraph()->lineHeightOfChar(cursor->index());
+ r = QRect(r.x(), r.y() + cursor->y(), r.width(), h);
+ }
+ r.moveBy(-contentsX(), -contentsY());
+ viewport()->update(r);
+}
+
+enum {
+ IdUndo = 0,
+ IdRedo = 1,
+ IdCut = 2,
+ IdCopy = 3,
+ IdPaste = 4,
+ IdClear = 5,
+ IdSelectAll = 6
+};
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void Q3TextEdit::contentsWheelEvent(QWheelEvent *e)
+{
+ if (isReadOnly()) {
+ if (e->state() & Qt::ControlButton) {
+ if (e->delta() > 0)
+ zoomOut();
+ else if (e->delta() < 0)
+ zoomIn();
+ return;
+ }
+ }
+ Q3ScrollView::contentsWheelEvent(e);
+}
+#endif
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMousePressEvent(QMouseEvent *e)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimMousePressEvent(e);
+ return;
+ }
+#endif
+
+#if !defined(QT_NO_IM)
+ if (e->button() == Qt::LeftButton && d->preeditLength > 0 && cursor->paragraph()) {
+ Q3TextCursor c = *cursor;
+ placeCursor(e->pos(), &c, false);
+ inputContext()->mouseHandler(c.index() - d->preeditStart, e);
+ if (d->preeditLength > 0)
+ return;
+ }
+#endif
+
+ if (d->trippleClickTimer->isActive() &&
+ (e->globalPos() - d->trippleClickPoint).manhattanLength() <
+ QApplication::startDragDistance()) {
+ Q3TextCursor c1 = *cursor;
+ Q3TextCursor c2 = *cursor;
+ c1.gotoLineStart();
+ c2.gotoLineEnd();
+ doc->setSelectionStart(Q3TextDocument::Standard, c1);
+ doc->setSelectionEnd(Q3TextDocument::Standard, c2);
+ *cursor = c2;
+ repaintChanged();
+ mousePressed = true;
+ return;
+ }
+
+ clearUndoRedo();
+ Q3TextCursor oldCursor = *cursor;
+ Q3TextCursor c = *cursor;
+ mousePos = e->pos();
+ mightStartDrag = false;
+ pressedLink.clear();
+ d->pressedName.clear();
+
+ if (e->button() == Qt::LeftButton) {
+ mousePressed = true;
+ drawCursor(false);
+ placeCursor(e->pos());
+ ensureCursorVisible();
+
+ if (isReadOnly() && linksEnabled()) {
+ Q3TextCursor c = *cursor;
+ placeCursor(e->pos(), &c, true);
+ if (c.paragraph() && c.paragraph()->at(c.index()) &&
+ c.paragraph()->at(c.index())->isAnchor()) {
+ pressedLink = c.paragraph()->at(c.index())->anchorHref();
+ d->pressedName = c.paragraph()->at(c.index())->anchorName();
+ }
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ if (doc->inSelection(Q3TextDocument::Standard, e->pos())) {
+ mightStartDrag = true;
+ drawCursor(true);
+ dragStartTimer->start(QApplication::startDragTime(), true);
+ dragStartPos = e->pos();
+ return;
+ }
+#endif
+
+ bool redraw = false;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ if (!(e->state() & Qt::ShiftButton)) {
+ redraw = doc->removeSelection(Q3TextDocument::Standard);
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ } else {
+ redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
+ }
+ } else {
+ if (isReadOnly() || !(e->state() & Qt::ShiftButton)) {
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ } else {
+ doc->setSelectionStart(Q3TextDocument::Standard, c);
+ redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
+ }
+ }
+
+ for (int i = 1; i < doc->numSelections(); ++i) // start with 1 as we don't want to remove the Standard-Selection
+ redraw = doc->removeSelection(i) || redraw;
+
+ if (!redraw) {
+ drawCursor(true);
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ } else if (e->button() == Qt::MidButton) {
+ bool redraw = doc->removeSelection(Q3TextDocument::Standard);
+ if (!redraw) {
+ drawCursor(true);
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ }
+
+ if (*cursor != oldCursor)
+ updateCurrentFormat();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMouseMoveEvent(QMouseEvent *e)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimMouseMoveEvent(e);
+ return;
+ }
+#endif
+
+#if !defined(QT_NO_IM)
+ if (d->preeditLength > 0)
+ return;
+#endif
+
+ if (mousePressed) {
+#ifndef QT_NO_DRAGANDDROP
+ if (mightStartDrag) {
+ dragStartTimer->stop();
+ if ((e->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance())
+ startDrag();
+#ifndef QT_NO_CURSOR
+ if (!isReadOnly())
+ viewport()->setCursor(Qt::IBeamCursor);
+#endif
+ return;
+ }
+#endif
+ mousePos = e->pos();
+ handleMouseMove(mousePos);
+ oldMousePos = mousePos;
+ }
+
+#ifndef QT_NO_CURSOR
+ if (!isReadOnly() && !mousePressed) {
+ if (doc->hasSelection(Q3TextDocument::Standard) && doc->inSelection(Q3TextDocument::Standard, e->pos()))
+ viewport()->setCursor(Qt::ArrowCursor);
+ else
+ viewport()->setCursor(Qt::IBeamCursor);
+ }
+#endif
+ updateCursor(e->pos());
+}
+
+void Q3TextEdit::copyToClipboard()
+{
+#ifndef QT_NO_CLIPBOARD
+ if (QApplication::clipboard()->supportsSelection()) {
+ d->clipboard_mode = QClipboard::Selection;
+
+ // don't listen to selection changes
+ disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+ copy();
+ // listen to selection changes
+ connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
+ this, SLOT(clipboardChanged()));
+
+ d->clipboard_mode = QClipboard::Clipboard;
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMouseReleaseEvent(QMouseEvent * e)
+{
+ if (!inDoubleClick) { // could be the release of a dblclick
+ int para = 0;
+ int index = charAt(e->pos(), &para);
+ emit clicked(para, index);
+ }
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimMouseReleaseEvent(e);
+ return;
+ }
+#endif
+ Q3TextCursor oldCursor = *cursor;
+ if (scrollTimer->isActive())
+ scrollTimer->stop();
+#ifndef QT_NO_DRAGANDDROP
+ if (dragStartTimer->isActive())
+ dragStartTimer->stop();
+ if (mightStartDrag) {
+ selectAll(false);
+ mousePressed = false;
+ }
+#endif
+ if (mousePressed) {
+ mousePressed = false;
+ copyToClipboard();
+ }
+#ifndef QT_NO_CLIPBOARD
+ else if (e->button() == Qt::MidButton && !isReadOnly()) {
+ // only do middle-click pasting on systems that have selections (ie. X11)
+ if (QApplication::clipboard()->supportsSelection()) {
+ drawCursor(false);
+ placeCursor(e->pos());
+ ensureCursorVisible();
+ doc->setSelectionStart(Q3TextDocument::Standard, oldCursor);
+ bool redraw = false;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ redraw = doc->removeSelection(Q3TextDocument::Standard);
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ } else {
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ }
+ // start with 1 as we don't want to remove the Standard-Selection
+ for (int i = 1; i < doc->numSelections(); ++i)
+ redraw = doc->removeSelection(i) || redraw;
+ if (!redraw) {
+ drawCursor(true);
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(Qt::IBeamCursor);
+#endif
+ }
+ d->clipboard_mode = QClipboard::Selection;
+ paste();
+ d->clipboard_mode = QClipboard::Clipboard;
+ }
+ }
+#endif
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ if (oldCursor != *cursor)
+ updateCurrentFormat();
+ inDoubleClick = false;
+
+#ifndef QT_NO_NETWORKPROTOCOL
+ if (( (!onLink.isEmpty() && onLink == pressedLink)
+ || (!d->onName.isEmpty() && d->onName == d->pressedName))
+ && linksEnabled()) {
+ if (!onLink.isEmpty()) {
+ QUrl u = QUrl(doc->context()).resolved(onLink);
+ emitLinkClicked(u.toString(QUrl::None));
+ }
+ if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
+ emit browser->anchorClicked(d->onName, onLink);
+
+ // emitting linkClicked() may result in that the cursor winds
+ // up hovering over a different valid link - check this and
+ // set the appropriate cursor shape
+ updateCursor(e->pos());
+ }
+#endif
+ drawCursor(true);
+ if (!doc->hasSelection(Q3TextDocument::Standard, true))
+ doc->removeSelection(Q3TextDocument::Standard);
+
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ emit selectionChanged();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMouseDoubleClickEvent(QMouseEvent * e)
+{
+ if (e->button() != Qt::LeftButton) {
+ e->ignore();
+ return;
+ }
+#if !defined(QT_NO_IM)
+ if (d->preeditLength > 0)
+ return;
+#endif
+
+ int para = 0;
+ int index = charAt(e->pos(), &para);
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ QString str = d->od->lines[LOGOFFSET(para)];
+ int startIdx = index, endIdx = index, i;
+ if (!str[index].isSpace()) {
+ i = startIdx;
+ // find start of word
+ while (i >= 0 && !str[i].isSpace()) {
+ startIdx = i--;
+ }
+ i = endIdx;
+ // find end of word..
+ while (i < str.length() && !str[i].isSpace()) {
+ endIdx = ++i;
+ }
+ // ..and start of next
+ while (i < str.length() && str[i].isSpace()) {
+ endIdx = ++i;
+ }
+ optimSetSelection(para, startIdx, para, endIdx);
+ repaintContents();
+ }
+ } else
+#endif
+ {
+ Q3TextCursor c1 = *cursor;
+ Q3TextCursor c2 = *cursor;
+#if defined(Q_OS_MAC)
+ Q3TextParagraph *para = cursor->paragraph();
+ if (cursor->isValid()) {
+ if (para->at(cursor->index())->c.isLetterOrNumber()) {
+ while (c1.index() > 0 &&
+ c1.paragraph()->at(c1.index()-1)->c.isLetterOrNumber())
+ c1.gotoPreviousLetter();
+ while (c2.paragraph()->at(c2.index())->c.isLetterOrNumber() &&
+ !c2.atParagEnd())
+ c2.gotoNextLetter();
+ } else if (para->at(cursor->index())->c.isSpace()) {
+ while (c1.index() > 0 &&
+ c1.paragraph()->at(c1.index()-1)->c.isSpace())
+ c1.gotoPreviousLetter();
+ while (c2.paragraph()->at(c2.index())->c.isSpace() &&
+ !c2.atParagEnd())
+ c2.gotoNextLetter();
+ } else if (!c2.atParagEnd()) {
+ c2.gotoNextLetter();
+ }
+ }
+#else
+ if (cursor->index() > 0 && !cursor->paragraph()->at(cursor->index()-1)->c.isSpace())
+ c1.gotoPreviousWord();
+ if (!cursor->paragraph()->at(cursor->index())->c.isSpace() && !cursor->atParagEnd())
+ c2.gotoNextWord();
+#endif
+ doc->setSelectionStart(Q3TextDocument::Standard, c1);
+ doc->setSelectionEnd(Q3TextDocument::Standard, c2);
+
+ *cursor = c2;
+
+ repaintChanged();
+
+ d->trippleClickTimer->start(qApp->doubleClickInterval(), true);
+ d->trippleClickPoint = e->globalPos();
+ }
+ inDoubleClick = true;
+ mousePressed = true;
+ emit doubleClicked(para, index);
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+ e->acceptAction();
+ inDnD = true;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+ drawCursor(false);
+ placeCursor(e->pos(), cursor);
+ drawCursor(true);
+ e->acceptAction();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDragLeaveEvent(QDragLeaveEvent *)
+{
+ drawCursor(false);
+ inDnD = false;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDropEvent(QDropEvent *e)
+{
+ if (isReadOnly())
+ return;
+ inDnD = false;
+ e->acceptAction();
+ bool intern = false;
+ if (Q3RichTextDrag::canDecode(e)) {
+ bool hasSel = doc->hasSelection(Q3TextDocument::Standard);
+ bool internalDrag = e->source() == this || e->source() == viewport();
+ int dropId, dropIndex;
+ Q3TextCursor insertCursor = *cursor;
+ dropId = cursor->paragraph()->paragId();
+ dropIndex = cursor->index();
+ if (hasSel && internalDrag) {
+ Q3TextCursor c1, c2;
+ int selStartId, selStartIndex;
+ int selEndId, selEndIndex;
+ c1 = doc->selectionStartCursor(Q3TextDocument::Standard);
+ c1.restoreState();
+ c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
+ c2.restoreState();
+ selStartId = c1.paragraph()->paragId();
+ selStartIndex = c1.index();
+ selEndId = c2.paragraph()->paragId();
+ selEndIndex = c2.index();
+ if (((dropId > selStartId) ||
+ (dropId == selStartId && dropIndex > selStartIndex)) &&
+ ((dropId < selEndId) ||
+ (dropId == selEndId && dropIndex <= selEndIndex)))
+ insertCursor = c1;
+ if (dropId == selEndId && dropIndex > selEndIndex) {
+ insertCursor = c1;
+ if (selStartId == selEndId) {
+ insertCursor.setIndex(dropIndex -
+ (selEndIndex - selStartIndex));
+ } else {
+ insertCursor.setIndex(dropIndex - selEndIndex +
+ selStartIndex);
+ }
+ }
+ }
+
+ if (internalDrag && e->action() == QDropEvent::Move) {
+ removeSelectedText();
+ intern = true;
+ doc->removeSelection(Q3TextDocument::Standard);
+ } else {
+ doc->removeSelection(Q3TextDocument::Standard);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ drawCursor(false);
+ cursor->setParagraph(insertCursor.paragraph());
+ cursor->setIndex(insertCursor.index());
+ drawCursor(true);
+ if (!cursor->nestedDepth()) {
+ QString subType = QLatin1String("plain");
+ if (textFormat() != Qt::PlainText) {
+ if (e->provides("application/x-qrichtext"))
+ subType = QLatin1String("x-qrichtext");
+ }
+#ifndef QT_NO_CLIPBOARD
+ pasteSubType(subType.toLatin1(), e);
+#endif
+ // emit appropriate signals.
+ emit selectionChanged();
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ } else {
+ if (intern)
+ undo();
+ e->ignore();
+ }
+ }
+}
+
+#endif
+
+/*!
+ \reimp
+*/
+void Q3TextEdit::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+ clearUndoRedo();
+ mousePressed = false;
+
+ e->accept();
+#ifndef QT_NO_POPUPMENU
+ Q3PopupMenu *popup = createPopupMenu(e->pos());
+ if (!popup)
+ popup = createPopupMenu();
+ if (!popup)
+ return;
+ int r = popup->exec(e->globalPos(), -1);
+ delete popup;
+
+ if (r == d->id[IdClear])
+ clear();
+ else if (r == d->id[IdSelectAll]) {
+ selectAll();
+#ifndef QT_NO_CLIPBOARD
+ // if the clipboard support selections, put the newly selected text into
+ // the clipboard
+ if (QApplication::clipboard()->supportsSelection()) {
+ d->clipboard_mode = QClipboard::Selection;
+
+ // don't listen to selection changes
+ disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+ copy();
+ // listen to selection changes
+ connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
+ this, SLOT(clipboardChanged()));
+
+ d->clipboard_mode = QClipboard::Clipboard;
+ }
+#endif
+ } else if (r == d->id[IdUndo])
+ undo();
+ else if (r == d->id[IdRedo])
+ redo();
+#ifndef QT_NO_CLIPBOARD
+ else if (r == d->id[IdCut])
+ cut();
+ else if (r == d->id[IdCopy])
+ copy();
+ else if (r == d->id[IdPaste])
+ paste();
+#endif
+#endif
+}
+
+
+void Q3TextEdit::autoScrollTimerDone()
+{
+ if (mousePressed)
+ handleMouseMove( viewportToContents(viewport()->mapFromGlobal(QCursor::pos()) ));
+}
+
+void Q3TextEdit::handleMouseMove(const QPoint& pos)
+{
+ if (!mousePressed)
+ return;
+
+ if ((!scrollTimer->isActive() && pos.y() < contentsY()) || pos.y() > contentsY() + visibleHeight())
+ scrollTimer->start(100, false);
+ else if (scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight())
+ scrollTimer->stop();
+
+ drawCursor(false);
+ Q3TextCursor oldCursor = *cursor;
+
+ placeCursor(pos);
+
+ if (inDoubleClick) {
+ Q3TextCursor cl = *cursor;
+ cl.gotoPreviousWord();
+ Q3TextCursor cr = *cursor;
+ cr.gotoNextWord();
+
+ int diff = QABS(oldCursor.paragraph()->at(oldCursor.index())->x - mousePos.x());
+ int ldiff = QABS(cl.paragraph()->at(cl.index())->x - mousePos.x());
+ int rdiff = QABS(cr.paragraph()->at(cr.index())->x - mousePos.x());
+
+
+ if (cursor->paragraph()->lineStartOfChar(cursor->index()) !=
+ oldCursor.paragraph()->lineStartOfChar(oldCursor.index()))
+ diff = 0xFFFFFF;
+
+ if (rdiff < diff && rdiff < ldiff)
+ *cursor = cr;
+ else if (ldiff < diff && ldiff < rdiff)
+ *cursor = cl;
+ else
+ *cursor = oldCursor;
+
+ }
+ ensureCursorVisible();
+
+ bool redraw = false;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
+ }
+
+ if (!redraw) {
+ drawCursor(true);
+ } else {
+ repaintChanged();
+ drawCursor(true);
+ }
+
+ if (currentFormat && currentFormat->key() != cursor->paragraph()->at(cursor->index())->format()->key()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(cursor->index())->format());
+ if (currentFormat->isMisspelled()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color());
+ }
+ emit currentFontChanged(currentFormat->font());
+ emit currentColorChanged(currentFormat->color());
+ emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
+ }
+
+ if (currentAlignment != cursor->paragraph()->alignment()) {
+ currentAlignment = cursor->paragraph()->alignment();
+ block_set_alignment = true;
+ emit currentAlignmentChanged(currentAlignment);
+ block_set_alignment = false;
+ }
+}
+
+/*! \internal */
+
+void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c, bool link)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+ if (!c)
+ c = cursor;
+
+ if(c == cursor)
+ resetInputContext();
+ c->restoreState();
+ Q3TextParagraph *s = doc->firstParagraph();
+ c->place(pos, s, link);
+}
+
+
+QVariant Q3TextEdit::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q3TextCursor c(*cursor);
+
+ switch(query) {
+ case Qt::ImMicroFocus: {
+ int h = c.paragraph()->lineHeightOfChar(cursor->index());
+ return QRect(c.x() - contentsX() + frameWidth(),
+ c.y() + cursor->paragraph()->rect().y() - contentsY() + frameWidth(), 1, h);
+ }
+ case Qt::ImFont:
+ return c.paragraph()->at(c.index())->format()->font();
+ default:
+ // ##### fix the others!
+ return QWidget::inputMethodQuery(query);
+ }
+}
+
+
+
+void Q3TextEdit::formatMore()
+{
+ if (!lastFormatted)
+ return;
+
+ int bottom = contentsHeight();
+ int lastTop = -1;
+ int lastBottom = -1;
+ int to = 20;
+ bool firstVisible = false;
+ QRect cr(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ for (int i = 0; lastFormatted &&
+ (i < to || (firstVisible && lastTop < contentsY()+height()));
+ i++) {
+ lastFormatted->format();
+ lastTop = lastFormatted->rect().top();
+ lastBottom = lastFormatted->rect().bottom();
+ if (i == 0)
+ firstVisible = lastBottom < cr.bottom();
+ bottom = qMax(bottom, lastBottom);
+ lastFormatted = lastFormatted->next();
+ }
+
+ if (bottom > contentsHeight()) {
+ resizeContents(contentsWidth(), qMax(doc->height(), bottom));
+ } else if (!lastFormatted && lastBottom < contentsHeight()) {
+ resizeContents(contentsWidth(), qMax(doc->height(), lastBottom));
+ if (contentsHeight() < visibleHeight())
+ updateContents(0, contentsHeight(), visibleWidth(),
+ visibleHeight() - contentsHeight());
+ }
+
+ if (lastFormatted)
+ formatTimer->start(interval, true);
+ else
+ interval = qMax(0, interval);
+}
+
+void Q3TextEdit::doResize()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (!d->optimMode)
+#endif
+ {
+ if (wrapMode == FixedPixelWidth)
+ return;
+ doc->setMinimumWidth(-1);
+ resizeContents(0, 0);
+ doc->setWidth(visibleWidth());
+ doc->invalidate();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+ }
+ repaintContents();
+}
+
+/*! \internal */
+
+void Q3TextEdit::doChangeInterval()
+{
+ interval = 0;
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3TextEdit::eventFilter(QObject *o, QEvent *e)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (!d->optimMode && (o == this || o == viewport())) {
+#else
+ if (o == this || o == viewport()) {
+#endif
+ if (d->cursorBlinkActive && e->type() == QEvent::FocusIn) {
+ if (QApplication::cursorFlashTime() > 0)
+ blinkTimer->start(QApplication::cursorFlashTime() / 2);
+ drawCursor(true);
+ } else if (e->type() == QEvent::FocusOut) {
+ blinkTimer->stop();
+ drawCursor(false);
+ }
+ }
+
+ if (o == this && e->type() == QEvent::PaletteChange) {
+ QColor old(viewport()->palette().color(QPalette::Text));
+ if (old != palette().color(QPalette::Text)) {
+ QColor c(palette().color(QPalette::Text));
+ doc->setMinimumWidth(-1);
+ doc->setDefaultFormat(doc->formatCollection()->defaultFormat()->font(), c);
+ lastFormatted = doc->firstParagraph();
+ formatMore();
+ repaintChanged();
+ }
+ }
+
+ return Q3ScrollView::eventFilter(o, e);
+}
+
+/*!
+ Inserts the given \a text. If \a indent is true the paragraph that
+ contains the text is reindented; if \a checkNewLine is true the \a
+ text is checked for newlines and relaid out. If \a removeSelected
+ is true and there is a selection, the insertion replaces the
+ selected text.
+ */
+void Q3TextEdit::insert(const QString &text, bool indent,
+ bool checkNewLine, bool removeSelected)
+{
+ uint f = 0;
+ if (indent)
+ f |= RedoIndentation;
+ if (checkNewLine)
+ f |= CheckNewLines;
+ if (removeSelected)
+ f |= RemoveSelected;
+ insert(text, f);
+}
+
+/*!
+ Inserts \a text at the current cursor position.
+
+ The \a insertionFlags define how the text is inserted. If \c
+ RedoIndentation is set, the paragraph is re-indented. If \c
+ CheckNewLines is set, newline characters in \a text result in hard
+ line breaks (i.e. new paragraphs). If \c checkNewLine is not set,
+ the behavior of the editor is undefined if the \a text contains
+ newlines. (It is not possible to change Q3TextEdit's newline handling
+ behavior, but you can use QString::replace() to preprocess text
+ before inserting it.) If \c RemoveSelected is set, any selected
+ text (in selection 0) is removed before the text is inserted.
+
+ The default flags are \c CheckNewLines | \c RemoveSelected.
+
+ If the widget is in Qt::LogText mode this function will do nothing.
+
+ \sa paste() pasteSubType()
+*/
+
+
+void Q3TextEdit::insert(const QString &text, uint insertionFlags)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+
+ if (cursor->nestedDepth() != 0) // #### for 3.0, disable editing of tables as this is not advanced enough
+ return;
+
+ bool indent = insertionFlags & RedoIndentation;
+ bool checkNewLine = insertionFlags & CheckNewLines;
+ bool removeSelected = insertionFlags & RemoveSelected;
+ QString txt(text);
+ drawCursor(false);
+ if (!isReadOnly() && doc->hasSelection(Q3TextDocument::Standard) && removeSelected)
+ removeSelectedText();
+ Q3TextCursor c2 = *cursor;
+ int oldLen = 0;
+
+ if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) {
+ checkUndoRedoInfo(UndoRedoInfo::Insert);
+
+ // If we are inserting at the end of the previous insertion, we keep this in
+ // the same undo/redo command. Otherwise, we separate them in two different commands.
+ if (undoRedoInfo.valid() && undoRedoInfo.index + undoRedoInfo.d->text.length() != cursor->index()) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Insert;
+ }
+
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ oldLen = undoRedoInfo.d->text.length();
+ }
+
+ lastFormatted = checkNewLine && cursor->paragraph()->prev() ?
+ cursor->paragraph()->prev() : cursor->paragraph();
+ Q3TextCursor oldCursor = *cursor;
+ cursor->insert(txt, checkNewLine);
+ if (doc->useFormatCollection() && !doc->preProcessor()) {
+ doc->setSelectionStart(Q3TextDocument::Temp, oldCursor);
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ doc->setFormat(Q3TextDocument::Temp, currentFormat, Q3TextFormat::Format);
+ doc->removeSelection(Q3TextDocument::Temp);
+ }
+
+ if (indent && (txt == QString(QLatin1Char('{')) || txt == QString(QLatin1Char('}')) || txt == QString(QLatin1Char(':')) || txt == QString(QLatin1Char('#'))))
+ cursor->indent();
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+
+ if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) {
+ undoRedoInfo.d->text += txt;
+ if (!doc->preProcessor()) {
+ for (int i = 0; i < (int)txt.length(); ++i) {
+ if (txt[i] != QLatin1Char('\n') && c2.paragraph()->at(c2.index())->format()) {
+ c2.paragraph()->at(c2.index())->format()->addRef();
+ undoRedoInfo.d->text.
+ setFormat(oldLen + i,
+ c2.paragraph()->at(c2.index())->format(), true);
+ }
+ c2.gotoNextLetter();
+ }
+ }
+ }
+
+ if (!removeSelected) {
+ doc->setSelectionStart(Q3TextDocument::Standard, oldCursor);
+ doc->setSelectionEnd(Q3TextDocument::Standard, *cursor);
+ repaintChanged();
+ }
+
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Inserts \a text in the paragraph \a para at position \a index.
+*/
+
+void Q3TextEdit::insertAt(const QString &text, int para, int index)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimInsert(text, para, index);
+ return;
+ }
+#endif
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+ removeSelection(Q3TextDocument::Standard);
+ Q3TextCursor tmp = *cursor;
+ cursor->setParagraph(p);
+ cursor->setIndex(index);
+ insert(text, false, true, false);
+ *cursor = tmp;
+ removeSelection(Q3TextDocument::Standard);
+}
+
+/*!
+ Inserts \a text as a new paragraph at position \a para. If \a para
+ is -1, the text is appended. Use append() if the append operation
+ is performance critical.
+*/
+
+void Q3TextEdit::insertParagraph(const QString &text, int para)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimInsert(text + QLatin1Char('\n'), para, 0);
+ return;
+ }
+#endif
+ for (int i = 0; i < (int)doc->numSelections(); ++i)
+ doc->removeSelection(i);
+
+ Q3TextParagraph *p = doc->paragAt(para);
+
+ bool append = !p;
+ if (!p)
+ p = doc->lastParagraph();
+
+ Q3TextCursor old = *cursor;
+ drawCursor(false);
+
+ cursor->setParagraph(p);
+ cursor->setIndex(0);
+ clearUndoRedo();
+ qtextedit_ignore_readonly = true;
+ if (append && cursor->paragraph()->length() > 1) {
+ cursor->setIndex(cursor->paragraph()->length() - 1);
+ doKeyboardAction(ActionReturn);
+ }
+ insert(text, false, true, true);
+ doKeyboardAction(ActionReturn);
+ qtextedit_ignore_readonly = false;
+
+ drawCursor(false);
+ *cursor = old;
+ drawCursor(true);
+
+ repaintChanged();
+}
+
+/*!
+ Removes the paragraph \a para.
+*/
+
+void Q3TextEdit::removeParagraph(int para)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+
+ for (int i = 0; i < doc->numSelections(); ++i)
+ doc->removeSelection(i);
+
+ Q3TextCursor start(doc);
+ Q3TextCursor end(doc);
+ start.setParagraph(p);
+ start.setIndex(0);
+ end.setParagraph(p);
+ end.setIndex(p->length() - 1);
+
+ if (!(p == doc->firstParagraph() && p == doc->lastParagraph())) {
+ if (p->next()) {
+ end.setParagraph(p->next());
+ end.setIndex(0);
+ } else if (p->prev()) {
+ start.setParagraph(p->prev());
+ start.setIndex(p->prev()->length() - 1);
+ }
+ }
+
+ doc->setSelectionStart(Q3TextDocument::Temp, start);
+ doc->setSelectionEnd(Q3TextDocument::Temp, end);
+ removeSelectedText(Q3TextDocument::Temp);
+}
+
+/*!
+ Undoes the last operation.
+
+ If there is no operation to undo, i.e. there is no undo step in
+ the undo/redo history, nothing happens.
+
+ \sa undoAvailable() redo() undoDepth()
+*/
+
+void Q3TextEdit::undo()
+{
+ clearUndoRedo();
+ if (isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled)
+ return;
+
+ for (int i = 0; i < (int)doc->numSelections(); ++i)
+ doc->removeSelection(i);
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+
+ clearUndoRedo();
+ drawCursor(false);
+ Q3TextCursor *c = doc->undo(cursor);
+ if (!c) {
+ drawCursor(true);
+ return;
+ }
+ lastFormatted = 0;
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+ setModified();
+ // ### If we get back to a completely blank textedit, it
+ // is possible that cursor is invalid and further actions
+ // might not fix the problem, so reset the cursor here.
+ // This is copied from removeSeletedText(), it might be
+ // okay to just call that.
+ if (!cursor->isValid()) {
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ drawCursor(true);
+ repaintContents();
+ }
+ emit undoAvailable(isUndoAvailable());
+ emit redoAvailable(isRedoAvailable());
+ emit textChanged();
+}
+
+/*!
+ Redoes the last operation.
+
+ If there is no operation to redo, i.e. there is no redo step in
+ the undo/redo history, nothing happens.
+
+ \sa redoAvailable() undo() undoDepth()
+*/
+
+void Q3TextEdit::redo()
+{
+ if (isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled)
+ return;
+
+ for (int i = 0; i < (int)doc->numSelections(); ++i)
+ doc->removeSelection(i);
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+
+ clearUndoRedo();
+ drawCursor(false);
+ Q3TextCursor *c = doc->redo(cursor);
+ if (!c) {
+ drawCursor(true);
+ return;
+ }
+ lastFormatted = 0;
+ ensureCursorVisible();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+ setModified();
+ emit undoAvailable(isUndoAvailable());
+ emit redoAvailable(isRedoAvailable());
+ emit textChanged();
+}
+
+/*!
+ Pastes the text from the clipboard into the text edit at the
+ current cursor position. Only plain text is pasted.
+
+ If there is no text in the clipboard nothing happens.
+
+ \sa pasteSubType() cut() Q3TextEdit::copy()
+*/
+
+void Q3TextEdit::paste()
+{
+#ifndef QT_NO_MIMECLIPBOARD
+ if (isReadOnly())
+ return;
+ QString subType = QLatin1String("plain");
+ if (textFormat() != Qt::PlainText) {
+ QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
+ if (!m)
+ return;
+ if (m->provides("application/x-qrichtext"))
+ subType = QLatin1String("x-qrichtext");
+ }
+
+ pasteSubType(subType.toLatin1());
+#endif
+}
+
+void Q3TextEdit::checkUndoRedoInfo(UndoRedoInfo::Type t)
+{
+ if (undoRedoInfo.valid() && t != undoRedoInfo.type) {
+ clearUndoRedo();
+ }
+ undoRedoInfo.type = t;
+}
+
+/*!
+ Repaints any paragraphs that have changed.
+
+ Although used extensively internally you shouldn't need to call
+ this yourself.
+*/
+void Q3TextEdit::repaintChanged()
+{
+ if (!updatesEnabled() || !viewport()->updatesEnabled())
+ return;
+
+ if (doc->firstParagraph())
+ lastFormatted = doc->firstParagraph();
+ updateContents(); // good enough until this class is rewritten
+}
+
+#ifndef QT_NO_MIME
+Q3TextDrag *Q3TextEdit::dragObject(QWidget *parent) const
+{
+ if (!doc->hasSelection(Q3TextDocument::Standard) ||
+ doc->selectedText(Q3TextDocument::Standard).isEmpty())
+ return 0;
+ if (textFormat() != Qt::RichText)
+ return new Q3TextDrag(doc->selectedText(Q3TextDocument::Standard), parent);
+ Q3RichTextDrag *drag = new Q3RichTextDrag(parent);
+ drag->setPlainText(doc->selectedText(Q3TextDocument::Standard));
+ drag->setRichText(doc->selectedText(Q3TextDocument::Standard, true));
+ return drag;
+}
+#endif
+
+/*!
+ Copies the selected text (from selection 0) to the clipboard and
+ deletes it from the text edit.
+
+ If there is no selected text (in selection 0) nothing happens.
+
+ \sa Q3TextEdit::copy() paste() pasteSubType()
+*/
+
+void Q3TextEdit::cut()
+{
+ if (isReadOnly())
+ return;
+ normalCopy();
+ removeSelectedText();
+}
+
+void Q3TextEdit::normalCopy()
+{
+#ifndef QT_NO_MIME
+ Q3TextDrag *drag = dragObject();
+ if (!drag)
+ return;
+#ifndef QT_NO_MIMECLIPBOARD
+ QApplication::clipboard()->setData(drag, d->clipboard_mode);
+#endif // QT_NO_MIMECLIPBOARD
+#endif // QT_NO_MIME
+}
+
+/*!
+ Copies any selected text (from selection 0) to the clipboard.
+
+ \sa hasSelectedText() copyAvailable()
+*/
+
+void Q3TextEdit::copy()
+{
+#ifndef QT_NO_CLIPBOARD
+# ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode && optimHasSelection())
+ QApplication::clipboard()->setText(optimSelectedText(), d->clipboard_mode);
+ else
+ normalCopy();
+# else
+ normalCopy();
+# endif
+#endif
+}
+
+/*!
+ \internal
+
+ Re-indents the current paragraph.
+*/
+
+void Q3TextEdit::indent()
+{
+ if (isReadOnly())
+ return;
+
+ drawCursor(false);
+ if (!doc->hasSelection(Q3TextDocument::Standard))
+ cursor->indent();
+ else
+ doc->indentSelection(Q3TextDocument::Standard);
+ repaintChanged();
+ drawCursor(true);
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Reimplemented to allow tabbing through links. If \a n is true the
+ tab moves the focus to the next child; if \a n is false the tab
+ moves the focus to the previous child. Returns true if the focus
+ was moved; otherwise returns false.
+ */
+
+bool Q3TextEdit::focusNextPrevChild(bool n)
+{
+ if (!isReadOnly() || !linksEnabled())
+ return false;
+ bool b = doc->focusNextPrevChild(n);
+ repaintChanged();
+ if (b) {
+ Q3TextParagraph *p = doc->focusIndicator.parag;
+ int start = doc->focusIndicator.start;
+ int len = doc->focusIndicator.len;
+
+ int y = p->rect().y();
+ while (p
+ && len == 0
+ && p->at(start)->isCustom()
+ && p->at(start)->customItem()->isNested()) {
+
+ Q3TextTable *t = (Q3TextTable*)p->at(start)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.count(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ Q3TextDocument *cellDoc = c->richText();
+ if ( cellDoc->hasFocusParagraph() ) {
+ y += c->geometry().y() + c->verticalAlignmentOffset();
+
+ p = cellDoc->focusIndicator.parag;
+ start = cellDoc->focusIndicator.start;
+ len = cellDoc->focusIndicator.len;
+ if ( p )
+ y += p->rect().y();
+
+ break;
+ }
+ }
+ }
+ setContentsPos( contentsX(), QMIN( y, contentsHeight() - visibleHeight() ) );
+ }
+ return b;
+}
+
+/*!
+ \internal
+
+ This functions sets the current format to \a f. Only the fields of \a
+ f which are specified by the \a flags are used.
+*/
+
+void Q3TextEdit::setFormat(Q3TextFormat *f, int flags)
+{
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ drawCursor(false);
+ Q3TextCursor c1 = doc->selectionStartCursor(Q3TextDocument::Standard);
+ c1.restoreState();
+ Q3TextCursor c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
+ c2.restoreState();
+ if (undoEnabled) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Format;
+ undoRedoInfo.id = c1.paragraph()->paragId();
+ undoRedoInfo.index = c1.index();
+ undoRedoInfo.eid = c2.paragraph()->paragId();
+ undoRedoInfo.eindex = c2.index();
+ readFormats(c1, c2, undoRedoInfo.d->text);
+ undoRedoInfo.format = f;
+ undoRedoInfo.flags = flags;
+ clearUndoRedo();
+ }
+ doc->setFormat(Q3TextDocument::Standard, f, flags);
+ repaintChanged();
+ formatMore();
+ drawCursor(true);
+ setModified();
+ emit textChanged();
+ }
+ if (currentFormat && currentFormat->key() != f->key()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(f);
+ if (currentFormat->isMisspelled()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(currentFormat->font(),
+ currentFormat->color());
+ }
+ emit currentFontChanged(currentFormat->font());
+ emit currentColorChanged(currentFormat->color());
+ emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
+ if (cursor->index() == cursor->paragraph()->length() - 1) {
+ currentFormat->addRef();
+ cursor->paragraph()->string()->setFormat(cursor->index(), currentFormat, true);
+ if (cursor->paragraph()->length() == 1) {
+ cursor->paragraph()->invalidate(0);
+ cursor->paragraph()->format();
+ repaintChanged();
+ }
+ }
+ }
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ Sets the paragraph style of the current paragraph
+ to \a dm. If \a dm is Q3StyleSheetItem::DisplayListItem, the
+ type of the list item is set to \a listStyle.
+
+ \sa setAlignment()
+*/
+
+void Q3TextEdit::setParagType(Q3StyleSheetItem::DisplayMode dm,
+ Q3StyleSheetItem::ListStyle listStyle)
+{
+ if (isReadOnly())
+ return;
+
+ drawCursor(false);
+ Q3TextParagraph *start = cursor->paragraph();
+ Q3TextParagraph *end = start;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph();
+ end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph();
+ if (end->paragId() < start->paragId())
+ return; // do not trust our selections
+ }
+
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+
+ while (start != end->next()) {
+ start->setListStyle(listStyle);
+ if (dm == Q3StyleSheetItem::DisplayListItem) {
+ start->setListItem(true);
+ if(start->listDepth() == 0)
+ start->setListDepth(1);
+ } else if (start->isListItem()) {
+ start->setListItem(false);
+ start->setListDepth(qMax(start->listDepth()-1, 0));
+ }
+ start = start->next();
+ }
+
+ clearUndoRedo();
+ repaintChanged();
+ formatMore();
+ drawCursor(true);
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Sets the alignment of the current paragraph to \a a. Valid
+ alignments are Qt::AlignLeft, Qt::AlignRight,
+ Qt::AlignJustify and Qt::AlignCenter (which centers
+ horizontally).
+*/
+
+void Q3TextEdit::setAlignment(int a)
+{
+ if (isReadOnly() || block_set_alignment)
+ return;
+
+ drawCursor(false);
+ Q3TextParagraph *start = cursor->paragraph();
+ Q3TextParagraph *end = start;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph();
+ end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph();
+ if (end->paragId() < start->paragId())
+ return; // do not trust our selections
+ }
+
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+
+ while (start != end->next()) {
+ start->setAlignment(a);
+ start = start->next();
+ }
+
+ clearUndoRedo();
+ repaintChanged();
+ formatMore();
+ drawCursor(true);
+ if (currentAlignment != a) {
+ currentAlignment = a;
+ emit currentAlignmentChanged(currentAlignment);
+ }
+ setModified();
+ emit textChanged();
+}
+
+void Q3TextEdit::updateCurrentFormat()
+{
+ int i = cursor->index();
+ if (i > 0)
+ --i;
+ if (doc->useFormatCollection() &&
+ (!currentFormat || currentFormat->key() != cursor->paragraph()->at(i)->format()->key())) {
+ if (currentFormat)
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(i)->format());
+ if (currentFormat->isMisspelled()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color());
+ }
+ emit currentFontChanged(currentFormat->font());
+ emit currentColorChanged(currentFormat->color());
+ emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
+ }
+
+ if (currentAlignment != cursor->paragraph()->alignment()) {
+ currentAlignment = cursor->paragraph()->alignment();
+ block_set_alignment = true;
+ emit currentAlignmentChanged(currentAlignment);
+ block_set_alignment = false;
+ }
+}
+
+/*!
+ If \a b is true sets the current format to italic; otherwise sets
+ the current format to non-italic.
+
+ \sa italic()
+*/
+
+void Q3TextEdit::setItalic(bool b)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setItalic(b);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Italic);
+}
+
+/*!
+ If \a b is true sets the current format to bold; otherwise sets
+ the current format to non-bold.
+
+ \sa bold()
+*/
+
+void Q3TextEdit::setBold(bool b)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setBold(b);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Bold);
+}
+
+/*!
+ If \a b is true sets the current format to underline; otherwise
+ sets the current format to non-underline.
+
+ \sa underline()
+*/
+
+void Q3TextEdit::setUnderline(bool b)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setUnderline(b);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Underline);
+}
+
+/*!
+ Sets the font family of the current format to \a fontFamily.
+
+ \sa family() setCurrentFont()
+*/
+
+void Q3TextEdit::setFamily(const QString &fontFamily)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setFamily(fontFamily);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Family);
+}
+
+/*!
+ Sets the point size of the current format to \a s.
+
+ Note that if \a s is zero or negative, the behavior of this
+ function is not defined.
+
+ \sa pointSize() setCurrentFont() setFamily()
+*/
+
+void Q3TextEdit::setPointSize(int s)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setPointSize(s);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Size);
+}
+
+/*!
+ Sets the color of the current format, i.e. of the text, to \a c.
+
+ \sa color() setPaper()
+*/
+
+void Q3TextEdit::setColor(const QColor &c)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setColor(c);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Color);
+}
+
+/*!
+ Sets the vertical alignment of the current format, i.e. of the
+ text, to \a a.
+
+ \sa color() setPaper()
+*/
+
+void Q3TextEdit::setVerticalAlignment(Q3TextEdit::VerticalAlignment a)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setVAlign((Q3TextFormat::VerticalAlignment)a);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::VAlign);
+}
+
+void Q3TextEdit::setFontInternal(const QFont &f_)
+{
+ QFont font = f_;
+ if (font.kerning())
+ font.setKerning(false);
+ Q3TextFormat f(*currentFormat);
+ f.setFont(font);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Font);
+}
+
+
+QString Q3TextEdit::text() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimText();
+#endif
+
+ Q3TextParagraph *p = doc->firstParagraph();
+ if (!p || (!p->next() && p->length() <= 1))
+ return QString::fromLatin1("");
+
+ if (isReadOnly())
+ return doc->originalText();
+ return doc->text();
+}
+
+/*!
+ \overload
+
+ Returns the text of paragraph \a para.
+
+ If textFormat() is Qt::RichText the text will contain HTML
+ formatting tags.
+*/
+
+QString Q3TextEdit::text(int para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode && (d->od->numLines >= para)) {
+ QString paraStr = d->od->lines[LOGOFFSET(para)];
+ if (paraStr.isEmpty())
+ paraStr = QLatin1Char('\n');
+ return paraStr;
+ } else
+#endif
+ return doc->text(para);
+}
+
+/*!
+ \overload
+
+ Changes the text of the text edit to the string \a text and the
+ context to \a context. Any previous text is removed.
+
+ \a text may be interpreted either as plain text or as rich text,
+ depending on the textFormat(). The default setting is Qt::AutoText,
+ i.e. the text edit auto-detects the format from \a text.
+
+ For rich text the rendering style and available tags are defined
+ by a styleSheet(); see Q3StyleSheet for details.
+
+ The optional \a context is a path which the text edit's
+ Q3MimeSourceFactory uses to resolve the locations of files and
+ images. (See \l{Q3TextEdit::Q3TextEdit()}.) It is passed to the text
+ edit's Q3MimeSourceFactory when quering data.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa text(), setTextFormat()
+*/
+
+void Q3TextEdit::setText(const QString &text, const QString &context)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimSetText(text);
+ return;
+ }
+#endif
+ if (!isModified() && isReadOnly() &&
+ this->context() == context && this->text() == text)
+ return;
+
+ emit undoAvailable(false);
+ emit redoAvailable(false);
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ lastFormatted = 0;
+ int oldCursorPos = cursor->index();
+ int oldCursorPar = cursor->paragraph()->paragId();
+ cursor->restoreState();
+ delete cursor;
+ doc->setText(text, context);
+
+ if (wrapMode == FixedPixelWidth) {
+ resizeContents(wrapWidth, 0);
+ doc->setWidth(wrapWidth);
+ doc->setMinimumWidth(wrapWidth);
+ } else {
+ doc->setMinimumWidth(-1);
+ resizeContents(0, 0);
+ }
+
+ lastFormatted = doc->firstParagraph();
+ cursor = new Q3TextCursor(doc);
+ updateContents();
+
+ if (isModified())
+ setModified(false);
+ emit textChanged();
+ if (cursor->index() != oldCursorPos || cursor->paragraph()->paragId() != oldCursorPar) {
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ }
+ formatMore();
+ updateCurrentFormat();
+ d->scrollToAnchor.clear();
+}
+
+/*!
+ \property Q3TextEdit::text
+ \brief the text edit's text
+
+ There is no default text.
+
+ On setting, any previous text is deleted.
+
+ The text may be interpreted either as plain text or as rich text,
+ depending on the textFormat(). The default setting is Qt::AutoText,
+ i.e. the text edit auto-detects the format of the text.
+
+ For richtext, calling text() on an editable Q3TextEdit will cause
+ the text to be regenerated from the textedit. This may mean that
+ the QString returned may not be exactly the same as the one that
+ was set.
+
+ \sa textFormat
+*/
+
+
+/*!
+ \property Q3TextEdit::readOnly
+ \brief whether the text edit is read-only
+
+ In a read-only text edit the user can only navigate through the
+ text and select text; modifying the text is not possible.
+
+ This property's default is false.
+*/
+
+/*!
+ Finds the next occurrence of the string, \a expr. Returns true if
+ \a expr was found; otherwise returns false.
+
+ If \a para and \a index are both 0 the search begins from the
+ current cursor position. If \a para and \a index are both not 0,
+ the search begins from the \c{*}\a{index} character position in the
+ \c{*}\a{para} paragraph.
+
+ If \a cs is true the search is case sensitive, otherwise it is
+ case insensitive. If \a wo is true the search looks for whole word
+ matches only; otherwise it searches for any matching text. If \a
+ forward is true (the default) the search works forward from the
+ starting position to the end of the text, otherwise it works
+ backwards to the beginning of the text.
+
+ If \a expr is found the function returns true. If \a index and \a
+ para are not 0, the number of the paragraph in which the first
+ character of the match was found is put into \c{*}\a{para}, and the
+ index position of that character within the paragraph is put into
+ \c{*}\a{index}.
+
+ If \a expr is not found the function returns false. If \a index
+ and \a para are not 0 and \a expr is not found, \c{*}\a{index}
+ and \c{*}\a{para} are undefined.
+
+ Please note that this function will make the next occurrence of
+ the string (if found) the current selection, and will thus
+ modify the cursor position.
+
+ Using the \a para and \a index parameters will not work correctly
+ in case the document contains tables.
+*/
+
+bool Q3TextEdit::find(const QString &expr, bool cs, bool wo, bool forward,
+ int *para, int *index)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimFind(expr, cs, wo, forward, para, index);
+#endif
+ drawCursor(false);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ Q3TextCursor findcur = *cursor;
+ if (para && index) {
+ if (doc->paragAt(*para))
+ findcur.gotoPosition(doc->paragAt(*para), *index);
+ else
+ findcur.gotoEnd();
+ } else if (doc->hasSelection(Q3TextDocument::Standard)){
+ // maks sure we do not find the same selection again
+ if (forward)
+ findcur.gotoNextLetter();
+ else
+ findcur.gotoPreviousLetter();
+ } else if (!forward && findcur.index() == 0 && findcur.paragraph() == findcur.topParagraph()) {
+ findcur.gotoEnd();
+ }
+ removeSelection(Q3TextDocument::Standard);
+ bool found = doc->find(findcur, expr, cs, wo, forward);
+ if (found) {
+ if (para)
+ *para = findcur.paragraph()->paragId();
+ if (index)
+ *index = findcur.index();
+ *cursor = findcur;
+ repaintChanged();
+ ensureCursorVisible();
+ }
+ drawCursor(true);
+ if (found) {
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ }
+ return found;
+}
+
+void Q3TextEdit::blinkCursor()
+{
+ bool cv = cursorVisible;
+ blinkCursorVisible = !blinkCursorVisible;
+ drawCursor(blinkCursorVisible);
+ cursorVisible = cv;
+}
+
+/*!
+ Sets the cursor to position \a index in paragraph \a para.
+
+ \sa getCursorPosition()
+*/
+
+void Q3TextEdit::setCursorPosition(int para, int index)
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+
+ if (index > p->length() - 1)
+ index = p->length() - 1;
+
+ drawCursor(false);
+ cursor->setParagraph(p);
+ cursor->setIndex(index);
+ ensureCursorVisible();
+ drawCursor(true);
+ updateCurrentFormat();
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+}
+
+/*!
+ This function sets the \c{*}\a{para} and \c{*}\a{index} parameters to the
+ current cursor position. \a para and \a index must not be 0.
+
+ \sa setCursorPosition()
+*/
+
+void Q3TextEdit::getCursorPosition(int *para, int *index) const
+{
+ if (!para || !index)
+ return;
+ *para = cursor->paragraph()->paragId();
+ *index = cursor->index();
+}
+
+/*!
+ Sets a selection which starts at position \a indexFrom in
+ paragraph \a paraFrom and ends at position \a indexTo in paragraph
+ \a paraTo.
+
+ Any existing selections which have a different id (\a selNum) are
+ left alone, but if an existing selection has the same id as \a
+ selNum it is removed and replaced by this selection.
+
+ Uses the selection settings of selection \a selNum. If \a selNum
+ is 0, this is the default selection.
+
+ The cursor is moved to the end of the selection if \a selNum is 0,
+ otherwise the cursor position remains unchanged.
+
+ \sa getSelection() selectedText
+*/
+
+void Q3TextEdit::setSelection(int paraFrom, int indexFrom,
+ int paraTo, int indexTo, int selNum)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimSetSelection(paraFrom, indexFrom, paraTo, indexTo);
+ repaintContents();
+ return;
+ }
+#endif
+ if (doc->hasSelection(selNum)) {
+ doc->removeSelection(selNum);
+ repaintChanged();
+ }
+ if (selNum > doc->numSelections() - 1)
+ doc->addSelection(selNum);
+ Q3TextParagraph *p1 = doc->paragAt(paraFrom);
+ if (!p1)
+ return;
+ Q3TextParagraph *p2 = doc->paragAt(paraTo);
+ if (!p2)
+ return;
+
+ if (indexFrom > p1->length() - 1)
+ indexFrom = p1->length() - 1;
+ if (indexTo > p2->length() - 1)
+ indexTo = p2->length() - 1;
+
+ drawCursor(false);
+ Q3TextCursor c = *cursor;
+ Q3TextCursor oldCursor = *cursor;
+ c.setParagraph(p1);
+ c.setIndex(indexFrom);
+ cursor->setParagraph(p2);
+ cursor->setIndex(indexTo);
+ doc->setSelectionStart(selNum, c);
+ doc->setSelectionEnd(selNum, *cursor);
+ repaintChanged();
+ ensureCursorVisible();
+ if (selNum != Q3TextDocument::Standard)
+ *cursor = oldCursor;
+ drawCursor(true);
+}
+
+/*!
+ If there is a selection, \c{*}\a{paraFrom} is set to the number of the
+ paragraph in which the selection begins and \c{*}\a{paraTo} is set to
+ the number of the paragraph in which the selection ends. (They
+ could be the same.) \c{*}\a{indexFrom} is set to the index at which the
+ selection begins within \c{*}\a{paraFrom}, and \c{*}\a{indexTo} is set to
+ the index at which the selection ends within \c{*}\a{paraTo}.
+
+ If there is no selection, \c{*}\a{paraFrom}, \c{*}\a{indexFrom},
+ \c{*}\a{paraTo} and \c{*}\a{indexTo} are all set to -1.
+
+ If \a paraFrom, \a indexFrom, \a paraTo or \a indexTo is 0 this
+ function does nothing.
+
+ The \a selNum is the number of the selection (multiple selections
+ are supported). It defaults to 0 (the default selection).
+
+ \sa setSelection() selectedText
+*/
+
+void Q3TextEdit::getSelection(int *paraFrom, int *indexFrom,
+ int *paraTo, int *indexTo, int selNum) const
+{
+ if (!paraFrom || !paraTo || !indexFrom || !indexTo)
+ return;
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ *paraFrom = d->od->selStart.line;
+ *paraTo = d->od->selEnd.line;
+ *indexFrom = d->od->selStart.index;
+ *indexTo = d->od->selEnd.index;
+ return;
+ }
+#endif
+ if (!doc->hasSelection(selNum)) {
+ *paraFrom = -1;
+ *indexFrom = -1;
+ *paraTo = -1;
+ *indexTo = -1;
+ return;
+ }
+
+ doc->selectionStart(selNum, *paraFrom, *indexFrom);
+ doc->selectionEnd(selNum, *paraTo, *indexTo);
+}
+
+/*!
+ \property Q3TextEdit::textFormat
+ \brief the text format: rich text, plain text, log text or auto text.
+
+ The text format is one of the following:
+ \list
+ \i Qt::PlainText - all characters, except newlines, are displayed
+ verbatim, including spaces. Whenever a newline appears in the text
+ the text edit inserts a hard line break and begins a new
+ paragraph.
+ \i Qt::RichText - rich text rendering. The available styles are
+ defined in the default stylesheet Q3StyleSheet::defaultSheet().
+ \i Qt::LogText - optimized mode for very large texts. Supports a very
+ limited set of formatting tags (color, bold, underline and italic
+ settings).
+ \i Qt::AutoText - this is the default. The text edit autodetects which
+ rendering style is best, Qt::PlainText or Qt::RichText. This is done
+ by using the Q3StyleSheet::mightBeRichText() function.
+ \endlist
+*/
+
+void Q3TextEdit::setTextFormat(Qt::TextFormat format)
+{
+ doc->setTextFormat(format);
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+Qt::TextFormat Q3TextEdit::textFormat() const
+{
+ return doc->textFormat();
+}
+
+/*!
+ Returns the number of paragraphs in the text; an empty textedit is always
+ considered to have one paragraph, so 1 is returned in this case.
+*/
+
+int Q3TextEdit::paragraphs() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ return d->od->numLines;
+ }
+#endif
+ return doc->lastParagraph()->paragId() + 1;
+}
+
+/*!
+ Returns the number of lines in paragraph \a para, or -1 if there
+ is no paragraph with index \a para.
+*/
+
+int Q3TextEdit::linesOfParagraph(int para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ if (d->od->numLines >= para)
+ return 1;
+ else
+ return -1;
+ }
+#endif
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return -1;
+ return p->lines();
+}
+
+/*!
+ Returns the length of the paragraph \a para (i.e. the number of
+ characters), or -1 if there is no paragraph with index \a para.
+
+ This function ignores newlines.
+*/
+
+int Q3TextEdit::paragraphLength(int para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ if (d->od->numLines >= para) {
+ if (d->od->lines[LOGOFFSET(para)].isEmpty()) // CR
+ return 1;
+ else
+ return d->od->lines[LOGOFFSET(para)].length();
+ }
+ return -1;
+ }
+#endif
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return -1;
+ return p->length() - 1;
+}
+
+/*!
+ Returns the number of lines in the text edit; this could be 0.
+
+ \warning This function may be slow. Lines change all the time
+ during word wrapping, so this function has to iterate over all the
+ paragraphs and get the number of lines from each one individually.
+*/
+
+int Q3TextEdit::lines() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ return d->od->numLines;
+ }
+#endif
+ Q3TextParagraph *p = doc->firstParagraph();
+ int l = 0;
+ while (p) {
+ l += p->lines();
+ p = p->next();
+ }
+
+ return l;
+}
+
+/*!
+ Returns the line number of the line in paragraph \a para in which
+ the character at position \a index appears. The \a index position is
+ relative to the beginning of the paragraph. If there is no such
+ paragraph or no such character at the \a index position (e.g. the
+ index is out of range) -1 is returned.
+*/
+
+int Q3TextEdit::lineOfChar(int para, int index)
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return -1;
+
+ int idx, line;
+ Q3TextStringChar *c = p->lineStartOfChar(index, &idx, &line);
+ if (!c)
+ return -1;
+
+ return line;
+}
+
+void Q3TextEdit::setModified(bool m)
+{
+ bool oldModified = modified;
+ modified = m;
+ if (modified && doc->oTextValid)
+ doc->invalidateOriginalText();
+ if (oldModified != modified)
+ emit modificationChanged(modified);
+}
+
+/*!
+ \property Q3TextEdit::modified
+ \brief whether the document has been modified by the user
+*/
+
+bool Q3TextEdit::isModified() const
+{
+ return modified;
+}
+
+void Q3TextEdit::setModified()
+{
+ if (!isModified())
+ setModified(true);
+}
+
+/*!
+ Returns true if the current format is italic; otherwise returns false.
+
+ \sa setItalic()
+*/
+
+bool Q3TextEdit::italic() const
+{
+ return currentFormat->font().italic();
+}
+
+/*!
+ Returns true if the current format is bold; otherwise returns false.
+
+ \sa setBold()
+*/
+
+bool Q3TextEdit::bold() const
+{
+ return currentFormat->font().bold();
+}
+
+/*!
+ Returns true if the current format is underlined; otherwise returns
+ false.
+
+ \sa setUnderline()
+*/
+
+bool Q3TextEdit::underline() const
+{
+ return currentFormat->font().underline();
+}
+
+/*!
+ Returns the font family of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+*/
+
+QString Q3TextEdit::family() const
+{
+ return currentFormat->font().family();
+}
+
+/*!
+ Returns the point size of the font of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+*/
+
+int Q3TextEdit::pointSize() const
+{
+ return currentFormat->font().pointSize();
+}
+
+/*!
+ Returns the color of the current format.
+
+ \sa setColor() setPaper()
+*/
+
+QColor Q3TextEdit::color() const
+{
+ return currentFormat->color();
+}
+
+/*!
+ Returns Q3ScrollView::font()
+
+ \warning In previous versions this function returned the font of
+ the current format. This lead to confusion. Please use
+ currentFont() instead.
+*/
+
+QFont Q3TextEdit::font() const
+{
+ return Q3ScrollView::font();
+}
+
+/*!
+ Returns the font of the current format.
+
+ \sa setCurrentFont() setFamily() setPointSize()
+*/
+
+QFont Q3TextEdit::currentFont() const
+{
+ return currentFormat->font();
+}
+
+
+/*!
+ Returns the alignment of the current paragraph.
+
+ \sa setAlignment()
+*/
+
+int Q3TextEdit::alignment() const
+{
+ return currentAlignment;
+}
+
+/*!
+ Returns the vertical alignment of the current format.
+
+ \sa setVerticalAlignment()
+*/
+
+Q3TextEdit::VerticalAlignment Q3TextEdit::verticalAlignment() const
+{
+ return (Q3TextEdit::VerticalAlignment) currentFormat->vAlign();
+}
+
+void Q3TextEdit::startDrag()
+{
+#ifndef QT_NO_DRAGANDDROP
+ mousePressed = false;
+ inDoubleClick = false;
+ Q3DragObject *drag = dragObject(viewport());
+ if (!drag)
+ return;
+ if (isReadOnly()) {
+ drag->dragCopy();
+ } else {
+ if (drag->drag() && Q3DragObject::target() != this && Q3DragObject::target() != viewport())
+ removeSelectedText();
+ }
+#endif
+}
+
+/*!
+ If \a select is true (the default), all the text is selected as
+ selection 0. If \a select is false any selected text is
+ unselected, i.e. the default selection (selection 0) is cleared.
+
+ \sa selectedText
+*/
+
+void Q3TextEdit::selectAll(bool select)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ if (select)
+ optimSelectAll();
+ else
+ optimRemoveSelection();
+ return;
+ }
+#endif
+ if (!select)
+ doc->removeSelection(Q3TextDocument::Standard);
+ else
+ doc->selectAll(Q3TextDocument::Standard);
+ repaintChanged();
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ emit selectionChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+}
+
+void Q3TextEdit::UndoRedoInfo::clear()
+{
+ if (valid()) {
+ if (type == Insert || type == Return)
+ doc->addCommand(new Q3TextInsertCommand(doc, id, index, d->text.rawData(), styleInformation));
+ else if (type == Format)
+ doc->addCommand(new Q3TextFormatCommand(doc, id, index, eid, eindex, d->text.rawData(), format, flags));
+ else if (type == Style)
+ doc->addCommand(new Q3TextStyleCommand(doc, id, eid, styleInformation));
+ else if (type != Invalid) {
+ doc->addCommand(new Q3TextDeleteCommand(doc, id, index, d->text.rawData(), styleInformation));
+ }
+ }
+ type = Invalid;
+ d->text.clear();
+ id = -1;
+ index = -1;
+ styleInformation = QByteArray();
+}
+
+
+/*!
+ If there is some selected text (in selection 0) it is deleted. If
+ there is no selected text (in selection 0) the character to the
+ right of the text cursor is deleted.
+
+ \sa removeSelectedText() cut()
+*/
+
+void Q3TextEdit::del()
+{
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ return;
+ }
+
+ doKeyboardAction(ActionDelete);
+}
+
+
+Q3TextEdit::UndoRedoInfo::UndoRedoInfo(Q3TextDocument *dc)
+ : type(Invalid), doc(dc)
+{
+ d = new QUndoRedoInfoPrivate;
+ d->text.clear();
+ id = -1;
+ index = -1;
+}
+
+Q3TextEdit::UndoRedoInfo::~UndoRedoInfo()
+{
+ delete d;
+}
+
+bool Q3TextEdit::UndoRedoInfo::valid() const
+{
+ return id >= 0 && type != Invalid;
+}
+
+/*!
+ \internal
+
+ Resets the current format to the default format.
+*/
+
+void Q3TextEdit::resetFormat()
+{
+ setAlignment(Qt::AlignAuto);
+ setParagType(Q3StyleSheetItem::DisplayBlock, Q3StyleSheetItem::ListDisc);
+ setFormat(doc->formatCollection()->defaultFormat(), Q3TextFormat::Format);
+}
+
+/*!
+ Returns the Q3StyleSheet which is being used by this text edit.
+
+ \sa setStyleSheet()
+*/
+
+Q3StyleSheet* Q3TextEdit::styleSheet() const
+{
+ return doc->styleSheet();
+}
+
+/*!
+ Sets the stylesheet to use with this text edit to \a styleSheet.
+ Changes will only take effect for new text added with setText() or
+ append().
+
+ \sa styleSheet()
+*/
+
+void Q3TextEdit::setStyleSheet(Q3StyleSheet* styleSheet)
+{
+ doc->setStyleSheet(styleSheet);
+}
+
+/*!
+ \property Q3TextEdit::paper
+ \brief the background (paper) brush.
+
+ The brush that is currently used to draw the background of the
+ text edit. The initial setting is an empty brush.
+*/
+
+void Q3TextEdit::setPaper(const QBrush& pap)
+{
+ doc->setPaper(new QBrush(pap));
+ if ( pap.pixmap() )
+ viewport()->setBackgroundPixmap( *pap.pixmap() );
+ QPalette pal = palette();
+ pal.setColor(QPalette::Window, pap.color());
+ setPalette(pal);
+ pal = viewport()->palette();
+ pal.setColor(QPalette::Window, pap.color());
+ viewport()->setPalette(pal);
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ // force a repaint of the entire viewport - using updateContents()
+ // would clip the coords to the content size
+ if (d->optimMode)
+ repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height());
+ else
+#endif
+ updateContents();
+}
+
+QBrush Q3TextEdit::paper() const
+{
+ if (doc->paper())
+ return *doc->paper();
+ return QBrush(palette().base());
+}
+
+/*!
+ \property Q3TextEdit::linkUnderline
+ \brief whether hypertext links will be underlined
+
+ If true (the default) hypertext links will be displayed
+ underlined. If false links will not be displayed underlined.
+*/
+
+void Q3TextEdit::setLinkUnderline(bool b)
+{
+ if (doc->underlineLinks() == b)
+ return;
+ doc->setUnderlineLinks(b);
+ repaintChanged();
+}
+
+bool Q3TextEdit::linkUnderline() const
+{
+ return doc->underlineLinks();
+}
+
+/*!
+ Sets the text edit's mimesource factory to \a factory. See
+ Q3MimeSourceFactory for further details.
+
+ \sa mimeSourceFactory()
+ */
+
+#ifndef QT_NO_MIME
+void Q3TextEdit::setMimeSourceFactory(Q3MimeSourceFactory* factory)
+{
+ doc->setMimeSourceFactory(factory);
+}
+
+/*!
+ Returns the Q3MimeSourceFactory which is being used by this text
+ edit.
+
+ \sa setMimeSourceFactory()
+*/
+
+Q3MimeSourceFactory* Q3TextEdit::mimeSourceFactory() const
+{
+ return doc->mimeSourceFactory();
+}
+#endif
+
+/*!
+ Returns how many pixels high the text edit needs to be to display
+ all the text if the text edit is \a w pixels wide.
+*/
+
+int Q3TextEdit::heightForWidth(int w) const
+{
+ int oldw = doc->width();
+ doc->doLayout(0, w);
+ int h = doc->height();
+ doc->setWidth(oldw);
+ doc->invalidate();
+ ((Q3TextEdit*)this)->formatMore();
+ return h;
+}
+
+/*!
+ Appends a new paragraph with \a text to the end of the text edit. Note that
+ the undo/redo history is cleared by this function, and no undo
+ history is kept for appends which makes them faster than
+ insert()s. If you want to append text which is added to the
+ undo/redo history as well, use insertParagraph().
+*/
+
+void Q3TextEdit::append(const QString &text)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimAppend(text);
+ return;
+ }
+#endif
+ // flush and clear the undo/redo stack if necessary
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ doc->removeSelection(Q3TextDocument::Standard);
+ Qt::TextFormat f = doc->textFormat();
+ if (f == Qt::AutoText) {
+ if (Q3StyleSheet::mightBeRichText(text))
+ f = Qt::RichText;
+ else
+ f = Qt::PlainText;
+ }
+
+ drawCursor(false);
+ Q3TextCursor oldc(*cursor);
+ ensureFormatted(doc->lastParagraph());
+ bool atBottom = contentsY() >= contentsHeight() - visibleHeight();
+ cursor->gotoEnd();
+ if (cursor->index() > 0)
+ cursor->splitAndInsertEmptyParagraph();
+ Q3TextCursor oldCursor2 = *cursor;
+
+ if (f == Qt::PlainText) {
+ cursor->insert(text, true);
+ if (doc->useFormatCollection() && !doc->preProcessor() &&
+ currentFormat != cursor->paragraph()->at( cursor->index() )->format()) {
+ doc->setSelectionStart( Q3TextDocument::Temp, oldCursor2 );
+ doc->setSelectionEnd( Q3TextDocument::Temp, *cursor );
+ doc->setFormat( Q3TextDocument::Temp, currentFormat, Q3TextFormat::Format );
+ doc->removeSelection( Q3TextDocument::Temp );
+ }
+ } else {
+ cursor->paragraph()->setListItem(false);
+ cursor->paragraph()->setListDepth(0);
+ if (cursor->paragraph()->prev())
+ cursor->paragraph()->prev()->invalidate(0); // vertical margins might have to change
+ doc->setRichTextInternal(text);
+ }
+ formatMore();
+ repaintChanged();
+ if (atBottom)
+ scrollToBottom();
+ *cursor = oldc;
+ if (!isReadOnly())
+ cursorVisible = true;
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ \property Q3TextEdit::hasSelectedText
+ \brief whether some text is selected in selection 0
+*/
+
+bool Q3TextEdit::hasSelectedText() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimHasSelection();
+ else
+#endif
+ return doc->hasSelection(Q3TextDocument::Standard);
+}
+
+/*!
+ \property Q3TextEdit::selectedText
+ \brief The selected text (from selection 0) or an empty string if
+ there is no currently selected text (in selection 0).
+
+ The text is always returned as Qt::PlainText if the textFormat() is
+ Qt::PlainText or Qt::AutoText, otherwise it is returned as HTML.
+
+ \sa hasSelectedText
+*/
+
+QString Q3TextEdit::selectedText() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimSelectedText();
+ else
+#endif
+ return doc->selectedText(Q3TextDocument::Standard, textFormat() == Qt::RichText);
+}
+
+bool Q3TextEdit::handleReadOnlyKeyEvent(QKeyEvent *e)
+{
+ switch(e->key()) {
+ case Qt::Key_Down:
+ setContentsPos(contentsX(), contentsY() + 10);
+ break;
+ case Qt::Key_Up:
+ setContentsPos(contentsX(), contentsY() - 10);
+ break;
+ case Qt::Key_Left:
+ setContentsPos(contentsX() - 10, contentsY());
+ break;
+ case Qt::Key_Right:
+ setContentsPos(contentsX() + 10, contentsY());
+ break;
+ case Qt::Key_PageUp:
+ setContentsPos(contentsX(), contentsY() - visibleHeight());
+ break;
+ case Qt::Key_PageDown:
+ setContentsPos(contentsX(), contentsY() + visibleHeight());
+ break;
+ case Qt::Key_Home:
+ setContentsPos(contentsX(), 0);
+ break;
+ case Qt::Key_End:
+ setContentsPos(contentsX(), contentsHeight() - visibleHeight());
+ break;
+ case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+#ifndef QT_NO_NETWORKPROTOCOL
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Space: {
+ if (!doc->focusIndicator.href.isEmpty()
+ || !doc->focusIndicator.name.isEmpty()) {
+ if (!doc->focusIndicator.href.isEmpty()) {
+ QUrl u = QUrl(doc->context()).resolved(doc->focusIndicator.href);
+ emitLinkClicked(u.toString(QUrl::None));
+ }
+ if (!doc->focusIndicator.name.isEmpty())
+ if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
+ emit browser->anchorClicked(doc->focusIndicator.name, doc->focusIndicator.href);
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ } break;
+#endif
+ default:
+ if (e->state() & Qt::ControlButton) {
+ switch (e->key()) {
+ case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+#ifdef Q_WS_WIN
+ case Qt::Key_Insert:
+ copy();
+ break;
+ case Qt::Key_A:
+ selectAll();
+ break;
+#endif
+ }
+
+ }
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns the context of the text edit. The context is a path which
+ the text edit's Q3MimeSourceFactory uses to resolve the locations
+ of files and images.
+
+ \sa text
+*/
+
+QString Q3TextEdit::context() const
+{
+ return doc->context();
+}
+
+/*!
+ \property Q3TextEdit::documentTitle
+ \brief the title of the document parsed from the text.
+
+ For Qt::PlainText the title will be an empty string. For \c
+ Qt::RichText the title will be the text between the \c{<title>} tags,
+ if present, otherwise an empty string.
+*/
+
+QString Q3TextEdit::documentTitle() const
+{
+ return doc->attributes()[QLatin1String("title")];
+}
+
+void Q3TextEdit::makeParagVisible(Q3TextParagraph *p)
+{
+ setContentsPos(contentsX(), qMin(p->rect().y(), contentsHeight() - visibleHeight()));
+}
+
+/*!
+ Scrolls the text edit to make the text at the anchor called \a
+ name visible, if it can be found in the document. If the anchor
+ isn't found no scrolling will occur. An anchor is defined using
+ the HTML anchor tag, e.g. \c{<a name="target">}.
+*/
+
+void Q3TextEdit::scrollToAnchor(const QString& name)
+{
+ if (!isVisible()) {
+ d->scrollToAnchor = name;
+ return;
+ }
+ if (name.isEmpty())
+ return;
+ sync();
+ Q3TextCursor cursor(doc);
+ Q3TextParagraph* last = doc->lastParagraph();
+ for (;;) {
+ Q3TextStringChar* c = cursor.paragraph()->at(cursor.index());
+ if(c->isAnchor()) {
+ QString a = c->anchorName();
+ if (a == name ||
+ (a.contains(QLatin1Char('#')) && a.split(QLatin1Char('#')).contains(name))) {
+ setContentsPos(contentsX(), qMin(cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight()));
+ break;
+ }
+ }
+ if (cursor.paragraph() == last && cursor.atParagEnd() )
+ break;
+ cursor.gotoNextLetter();
+ }
+}
+
+/*!
+ Returns the text for the attribute \a attr (Qt::AnchorHref by
+ default) if there is an anchor at position \a pos (in contents
+ coordinates); otherwise returns an empty string.
+*/
+
+QString Q3TextEdit::anchorAt(const QPoint& pos, Qt::AnchorAttribute attr)
+{
+ Q3TextCursor c(doc);
+ placeCursor(pos, &c, true);
+ switch(attr) {
+ case Qt::AnchorName:
+ return c.paragraph()->at(c.index())->anchorName();
+ case Qt::AnchorHref:
+ return c.paragraph()->at(c.index())->anchorHref();
+ }
+ // incase the compiler is really dumb about determining if a function
+ // returns something :)
+ return QString();
+}
+
+void Q3TextEdit::documentWidthChanged(int w)
+{
+ resizeContents(qMax(visibleWidth(), w), contentsHeight());
+}
+
+/*! \internal
+
+ This function does nothing
+*/
+
+void Q3TextEdit::updateStyles()
+{
+}
+
+void Q3TextEdit::setDocument(Q3TextDocument *dc)
+{
+ if (dc == 0) {
+ qWarning("Q3TextEdit::setDocument() called with null Q3TextDocument pointer");
+ return;
+ }
+ if (dc == doc)
+ return;
+ doc = dc;
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ clearUndoRedo();
+ undoRedoInfo.doc = doc;
+ lastFormatted = 0;
+}
+
+#ifndef QT_NO_CLIPBOARD
+
+/*!
+ Pastes the text with format \a subtype from the clipboard into the
+ text edit at the current cursor position. The \a subtype can be
+ "plain" or "html".
+
+ If there is no text with format \a subtype in the clipboard
+ nothing happens.
+
+ \sa paste() cut() Q3TextEdit::copy()
+*/
+
+void Q3TextEdit::pasteSubType(const QByteArray &subtype)
+{
+#ifndef QT_NO_MIMECLIPBOARD
+ QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
+ pasteSubType(subtype, m);
+#endif
+}
+
+/*! \internal */
+
+void Q3TextEdit::pasteSubType(const QByteArray& subtype, QMimeSource *m)
+{
+#ifndef QT_NO_MIME
+ QByteArray st = subtype;
+
+ if (subtype != "x-qrichtext")
+ st.prepend("text/");
+ else
+ st.prepend("application/");
+ if (!m)
+ return;
+ if (doc->hasSelection(Q3TextDocument::Standard))
+ removeSelectedText();
+ if (!Q3RichTextDrag::canDecode(m))
+ return;
+ QString t;
+ if (!Q3RichTextDrag::decode(m, t, QString::fromLatin1(st), QString::fromLatin1(subtype)))
+ return;
+ if (st == "application/x-qrichtext") {
+ int start;
+ if ((start = t.indexOf(QLatin1String("<!--StartFragment-->"))) != -1) {
+ start += 20;
+ int end = t.indexOf(QLatin1String("<!--EndFragment-->"));
+ Q3TextCursor oldC = *cursor;
+
+ // during the setRichTextInternal() call the cursors
+ // paragraph might get joined with the provious one, so
+ // the cursors one would get deleted and oldC.paragraph()
+ // would be a dnagling pointer. To avoid that try to go
+ // one letter back and later go one forward again.
+ oldC.gotoPreviousLetter();
+ bool couldGoBack = oldC != *cursor;
+ // first para might get deleted, so remember to reset it
+ bool wasAtFirst = oldC.paragraph() == doc->firstParagraph();
+
+ if (start < end)
+ t = t.mid(start, end - start);
+ else
+ t = t.mid(start);
+ lastFormatted = cursor->paragraph();
+ if (lastFormatted->prev())
+ lastFormatted = lastFormatted->prev();
+ doc->setRichTextInternal(t, cursor);
+
+ // the first para might have been deleted in
+ // setRichTextInternal(). To be sure, reset it if
+ // necessary.
+ if (wasAtFirst) {
+ int index = oldC.index();
+ oldC.setParagraph(doc->firstParagraph());
+ oldC.setIndex(index);
+ }
+
+ // if we went back one letter before (see last comment),
+ // go one forward to point to the right position
+ if (couldGoBack)
+ oldC.gotoNextLetter();
+
+ if (undoEnabled && !isReadOnly()) {
+ doc->setSelectionStart(Q3TextDocument::Temp, oldC);
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+
+ checkUndoRedoInfo(UndoRedoInfo::Insert);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = oldC.paragraph()->paragId();
+ undoRedoInfo.index = oldC.index();
+ undoRedoInfo.d->text.clear();
+ }
+ int oldLen = undoRedoInfo.d->text.length();
+ if (!doc->preProcessor()) {
+ QString txt = doc->selectedText(Q3TextDocument::Temp);
+ undoRedoInfo.d->text += txt;
+ for (int i = 0; i < (int)txt.length(); ++i) {
+ if (txt[i] != QLatin1Char('\n') && oldC.paragraph()->at(oldC.index())->format()) {
+ oldC.paragraph()->at(oldC.index())->format()->addRef();
+ undoRedoInfo.d->text.
+ setFormat(oldLen + i, oldC.paragraph()->at(oldC.index())->format(), true);
+ }
+ oldC.gotoNextLetter();
+ }
+ }
+ undoRedoInfo.clear();
+ removeSelection(Q3TextDocument::Temp);
+ }
+
+ formatMore();
+ setModified();
+ emit textChanged();
+ repaintChanged();
+ ensureCursorVisible();
+ return;
+ }
+ } else {
+#if defined(Q_OS_WIN32)
+ // Need to convert CRLF to LF
+ t.replace(QLatin1String("\r\n"), QLatin1String("\n"));
+#elif defined(Q_OS_MAC)
+ //need to convert CR to LF
+ t.replace(QLatin1Char('\r'), QLatin1Char('\n'));
+#endif
+ QChar *uc = (QChar *)t.unicode();
+ for (int i = 0; i < t.length(); i++) {
+ if (uc[i] < QLatin1Char(' ') && uc[i] != QLatin1Char('\n') && uc[i] != QLatin1Char('\t'))
+ uc[i] = QLatin1Char(' ');
+ }
+ if (!t.isEmpty())
+ insert(t, false, true);
+ }
+#endif //QT_NO_MIME
+}
+
+#ifndef QT_NO_MIMECLIPBOARD
+/*!
+ Prompts the user to choose a type from a list of text types
+ available, then copies text from the clipboard (if there is any)
+ into the text edit at the current text cursor position. Any
+ selected text (in selection 0) is first deleted.
+*/
+void Q3TextEdit::pasteSpecial(const QPoint& pt)
+{
+ QByteArray st = pickSpecial(QApplication::clipboard()->data(d->clipboard_mode),
+ true, pt);
+ if (!st.isEmpty())
+ pasteSubType(st);
+}
+#endif
+#ifndef QT_NO_MIME
+QByteArray Q3TextEdit::pickSpecial(QMimeSource* ms, bool always_ask, const QPoint& pt)
+{
+ if (ms) {
+#ifndef QT_NO_MENU
+ QMenu popup(this);
+ QString fmt;
+ int n = 0;
+ QHash<QString, bool> done;
+ for (int i = 0; !(fmt = QLatin1String(ms->format(i))).isNull(); i++) {
+ int semi = fmt.indexOf(QLatin1Char(';'));
+ if (semi >= 0)
+ fmt = fmt.left(semi);
+ if (fmt.left(5) == QLatin1String("text/")) {
+ fmt = fmt.mid(5);
+ if (!done.contains(fmt)) {
+ done.insert(fmt,true);
+ popup.insertItem(fmt, i);
+ n++;
+ }
+ }
+ }
+ if (n) {
+ QAction *action = (n == 1 && !always_ask)
+ ? popup.actions().at(0)
+ : popup.exec(pt);
+ if (action)
+ return action->text().toLatin1();
+ }
+#else
+ QString fmt;
+ for (int i = 0; !(fmt = ms->format(i)).isNull(); i++) {
+ int semi = fmt.indexOf(';');
+ if (semi >= 0)
+ fmt = fmt.left(semi);
+ if (fmt.left(5) == "text/") {
+ fmt = fmt.mid(5);
+ return fmt.latin1();
+ }
+ }
+#endif
+ }
+ return QByteArray();
+}
+#endif // QT_NO_MIME
+#endif // QT_NO_CLIPBOARD
+
+/*!
+ \enum Q3TextEdit::WordWrap
+
+ This enum defines the Q3TextEdit's word wrap modes.
+
+ \value NoWrap Do not wrap the text.
+
+ \value WidgetWidth Wrap the text at the current width of the
+ widget (this is the default). Wrapping is at whitespace by
+ default; this can be changed with setWrapPolicy().
+
+ \value FixedPixelWidth Wrap the text at a fixed number of pixels
+ from the widget's left side. The number of pixels is set with
+ wrapColumnOrWidth().
+
+ \value FixedColumnWidth Wrap the text at a fixed number of
+ character columns from the widget's left side. The number of
+ characters is set with wrapColumnOrWidth(). This is useful if you
+ need formatted text that can also be displayed gracefully on
+ devices with monospaced fonts, for example a standard VT100
+ terminal, where you might set wrapColumnOrWidth() to 80.
+
+ \sa setWordWrap() wordWrap()
+*/
+
+/*!
+ \property Q3TextEdit::wordWrap
+ \brief the word wrap mode
+
+ The default mode is \c WidgetWidth which causes words to be
+ wrapped at the right edge of the text edit. Wrapping occurs at
+ whitespace, keeping whole words intact. If you want wrapping to
+ occur within words use setWrapPolicy(). If you set a wrap mode of
+ \c FixedPixelWidth or \c FixedColumnWidth you should also call
+ setWrapColumnOrWidth() with the width you want.
+
+ \sa WordWrap, wrapColumnOrWidth, wrapPolicy,
+*/
+
+void Q3TextEdit::setWordWrap(WordWrap mode)
+{
+ if (wrapMode == mode)
+ return;
+ wrapMode = mode;
+ switch (mode) {
+ case NoWrap:
+ document()->formatter()->setWrapEnabled(false);
+ document()->formatter()->setWrapAtColumn(-1);
+ doc->setWidth(visibleWidth());
+ doc->setMinimumWidth(-1);
+ doc->invalidate();
+ updateContents();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+ break;
+ case WidgetWidth:
+ document()->formatter()->setWrapEnabled(true);
+ document()->formatter()->setWrapAtColumn(-1);
+ doResize();
+ break;
+ case FixedPixelWidth:
+ document()->formatter()->setWrapEnabled(true);
+ document()->formatter()->setWrapAtColumn(-1);
+ if (wrapWidth < 0)
+ wrapWidth = 200;
+ setWrapColumnOrWidth(wrapWidth);
+ break;
+ case FixedColumnWidth:
+ if (wrapWidth < 0)
+ wrapWidth = 80;
+ document()->formatter()->setWrapEnabled(true);
+ document()->formatter()->setWrapAtColumn(wrapWidth);
+ setWrapColumnOrWidth(wrapWidth);
+ break;
+ }
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+Q3TextEdit::WordWrap Q3TextEdit::wordWrap() const
+{
+ return wrapMode;
+}
+
+/*!
+ \property Q3TextEdit::wrapColumnOrWidth
+ \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped
+
+ If the wrap mode is \c FixedPixelWidth, the value is the number of
+ pixels from the left edge of the text edit at which text should be
+ wrapped. If the wrap mode is \c FixedColumnWidth, the value is the
+ column number (in character columns) from the left edge of the
+ text edit at which text should be wrapped.
+
+ \sa wordWrap
+*/
+void Q3TextEdit::setWrapColumnOrWidth(int value)
+{
+ wrapWidth = value;
+ if (wrapMode == FixedColumnWidth) {
+ document()->formatter()->setWrapAtColumn(wrapWidth);
+ resizeContents(0, 0);
+ doc->setWidth(visibleWidth());
+ doc->setMinimumWidth(-1);
+ } else if (wrapMode == FixedPixelWidth) {
+ document()->formatter()->setWrapAtColumn(-1);
+ resizeContents(wrapWidth, 0);
+ doc->setWidth(wrapWidth);
+ doc->setMinimumWidth(wrapWidth);
+ } else {
+ return;
+ }
+ doc->invalidate();
+ updateContents();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+}
+
+int Q3TextEdit::wrapColumnOrWidth() const
+{
+ if (wrapMode == WidgetWidth)
+ return visibleWidth();
+ return wrapWidth;
+}
+
+
+/*!
+ \enum Q3TextEdit::WrapPolicy
+
+ This enum defines where text can be wrapped in word wrap mode.
+
+ \value AtWhiteSpace Don't use this deprecated value (it is a
+ synonym for \c AtWordBoundary which you should use instead).
+ \value Anywhere Break anywhere, including within words.
+ \value AtWordBoundary Break lines at word boundaries, e.g. spaces or
+ newlines
+ \value AtWordOrDocumentBoundary Break lines at whitespace, e.g.
+ spaces or newlines if possible. Break it anywhere otherwise.
+
+ \sa setWrapPolicy()
+*/
+
+/*!
+ \property Q3TextEdit::wrapPolicy
+ \brief the word wrap policy, at whitespace or anywhere
+
+ Defines where text can be wrapped when word wrap mode is not \c
+ NoWrap. The choices are \c AtWordBoundary (the default), \c
+ Anywhere and \c AtWordOrDocumentBoundary
+
+ \sa wordWrap
+*/
+
+void Q3TextEdit::setWrapPolicy(WrapPolicy policy)
+{
+ if (wPolicy == policy)
+ return;
+ wPolicy = policy;
+ Q3TextFormatter *formatter;
+ if (policy == AtWordBoundary || policy == AtWordOrDocumentBoundary) {
+ formatter = new Q3TextFormatterBreakWords;
+ formatter->setAllowBreakInWords(policy == AtWordOrDocumentBoundary);
+ } else {
+ formatter = new Q3TextFormatterBreakInWords;
+ }
+ formatter->setWrapAtColumn(document()->formatter()->wrapAtColumn());
+ formatter->setWrapEnabled(document()->formatter()->isWrapEnabled(0));
+ document()->setFormatter(formatter);
+ doc->invalidate();
+ updateContents();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+}
+
+Q3TextEdit::WrapPolicy Q3TextEdit::wrapPolicy() const
+{
+ return wPolicy;
+}
+
+/*!
+ Deletes all the text in the text edit.
+
+ \sa cut() removeSelectedText() setText()
+*/
+
+void Q3TextEdit::clear()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimSetText(QLatin1String(""));
+ } else
+#endif
+ {
+ // make clear undoable
+ doc->selectAll(Q3TextDocument::Temp);
+ removeSelectedText(Q3TextDocument::Temp);
+ setContentsPos(0, 0);
+ if (cursor->isValid())
+ cursor->restoreState();
+ doc->clear(true);
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ lastFormatted = 0;
+ }
+ updateContents();
+
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+}
+
+int Q3TextEdit::undoDepth() const
+{
+ return document()->undoDepth();
+}
+
+/*!
+ \property Q3TextEdit::length
+ \brief the number of characters in the text
+*/
+
+int Q3TextEdit::length() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return d->od->len;
+ else
+#endif
+ return document()->length();
+}
+
+/*!
+ \property Q3TextEdit::tabStopWidth
+ \brief the tab stop width in pixels
+*/
+
+int Q3TextEdit::tabStopWidth() const
+{
+ return document()->tabStopWidth();
+}
+
+void Q3TextEdit::setUndoDepth(int d)
+{
+ document()->setUndoDepth(d);
+}
+
+void Q3TextEdit::setTabStopWidth(int ts)
+{
+ document()->setTabStops(ts);
+ doc->invalidate();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+ updateContents();
+}
+
+/*!
+ \reimp
+*/
+
+QSize Q3TextEdit::sizeHint() const
+{
+ // cf. Q3ScrollView::sizeHint()
+ ensurePolished();
+ int f = 2 * frameWidth();
+ int h = fontMetrics().height();
+ QSize sz(f, f);
+ return sz.expandedTo(QSize(12 * h, 8 * h));
+}
+
+void Q3TextEdit::clearUndoRedo()
+{
+ if (!undoEnabled)
+ return;
+ undoRedoInfo.clear();
+ emit undoAvailable(doc->commands()->isUndoAvailable());
+ emit redoAvailable(doc->commands()->isRedoAvailable());
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ This function gets the format of the character at position \a
+ index in paragraph \a para. Sets \a font to the character's font, \a
+ color to the character's color and \a verticalAlignment to the
+ character's vertical alignment.
+
+ Returns false if \a para or \a index is out of range otherwise
+ returns true.
+*/
+
+bool Q3TextEdit::getFormat(int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment)
+{
+ if (!font || !color)
+ return false;
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return false;
+ if (index < 0 || index >= p->length())
+ return false;
+ *font = p->at(index)->format()->font();
+ *color = p->at(index)->format()->color();
+ *verticalAlignment = (VerticalAlignment)p->at(index)->format()->vAlign();
+ return true;
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ This function gets the format of the paragraph \a para. Sets \a
+ font to the paragraphs's font, \a color to the paragraph's color, \a
+ verticalAlignment to the paragraph's vertical alignment, \a
+ alignment to the paragraph's alignment, \a displayMode to the
+ paragraph's display mode, \a listStyle to the paragraph's list style
+ (if the display mode is Q3StyleSheetItem::DisplayListItem) and \a
+ listDepth to the depth of the list (if the display mode is
+ Q3StyleSheetItem::DisplayListItem).
+
+ Returns false if \a para is out of range otherwise returns true.
+*/
+
+bool Q3TextEdit::getParagraphFormat(int para, QFont *font, QColor *color,
+ VerticalAlignment *verticalAlignment, int *alignment,
+ Q3StyleSheetItem::DisplayMode *displayMode,
+ Q3StyleSheetItem::ListStyle *listStyle,
+ int *listDepth)
+{
+ if (!font || !color || !alignment || !displayMode || !listStyle)
+ return false;
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return false;
+ *font = p->at(0)->format()->font();
+ *color = p->at(0)->format()->color();
+ *verticalAlignment = (VerticalAlignment)p->at(0)->format()->vAlign();
+ *alignment = p->alignment();
+ *displayMode = p->isListItem() ? Q3StyleSheetItem::DisplayListItem : Q3StyleSheetItem::DisplayBlock;
+ *listStyle = p->listStyle();
+ *listDepth = p->listDepth();
+ return true;
+}
+
+
+
+/*!
+ This function is called to create a right mouse button popup menu
+ at the document position \a pos. If you want to create a custom
+ popup menu, reimplement this function and return the created popup
+ menu. Ownership of the popup menu is transferred to the caller.
+
+ \warning The QPopupMenu ID values 0-7 are reserved, and they map to the
+ standard operations. When inserting items into your custom popup menu, be
+ sure to specify ID values larger than 7.
+*/
+
+Q3PopupMenu *Q3TextEdit::createPopupMenu(const QPoint& pos)
+{
+ Q_UNUSED(pos)
+#ifndef QT_NO_POPUPMENU
+ Q3PopupMenu *popup = new Q3PopupMenu(this, "qt_edit_menu");
+ if (!isReadOnly()) {
+ d->id[IdUndo] = popup->insertItem(tr("&Undo") + ACCEL_KEY(Z));
+ d->id[IdRedo] = popup->insertItem(tr("&Redo") + ACCEL_KEY(Y));
+ popup->addSeparator();
+ }
+#ifndef QT_NO_CLIPBOARD
+ if (!isReadOnly())
+ d->id[IdCut] = popup->insertItem(tr("Cu&t") + ACCEL_KEY(X));
+ d->id[IdCopy] = popup->insertItem(tr("&Copy") + ACCEL_KEY(C));
+ if (!isReadOnly())
+ d->id[IdPaste] = popup->insertItem(tr("&Paste") + ACCEL_KEY(V));
+#endif
+ if (!isReadOnly()) {
+ d->id[IdClear] = popup->insertItem(tr("Clear"));
+ popup->addSeparator();
+ }
+#if defined(Q_WS_X11)
+ d->id[IdSelectAll] = popup->insertItem(tr("Select All"));
+#else
+ d->id[IdSelectAll] = popup->insertItem(tr("Select All") + ACCEL_KEY(A));
+#endif
+ popup->setItemEnabled(d->id[IdUndo], !isReadOnly() && doc->commands()->isUndoAvailable());
+ popup->setItemEnabled(d->id[IdRedo], !isReadOnly() && doc->commands()->isRedoAvailable());
+#ifndef QT_NO_CLIPBOARD
+ popup->setItemEnabled(d->id[IdCut], !isReadOnly() && doc->hasSelection(Q3TextDocument::Standard, true));
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ popup->setItemEnabled(d->id[IdCopy], d->optimMode ? optimHasSelection() : doc->hasSelection(Q3TextDocument::Standard, true));
+#else
+ popup->setItemEnabled(d->id[IdCopy], doc->hasSelection(Q3TextDocument::Standard, true));
+#endif
+ popup->setItemEnabled(d->id[IdPaste], !isReadOnly() && !QApplication::clipboard()->text(d->clipboard_mode).isEmpty());
+#endif
+ const bool isEmptyDocument = (length() == 0);
+ popup->setItemEnabled(d->id[IdClear], !isReadOnly() && !isEmptyDocument);
+ popup->setItemEnabled(d->id[IdSelectAll], !isEmptyDocument);
+ return popup;
+#else
+ return 0;
+#endif
+}
+
+/*! \overload
+ This function is called to create a right mouse button popup menu.
+ If you want to create a custom popup menu, reimplement this function
+ and return the created popup menu. Ownership of the popup menu is
+ transferred to the caller.
+
+ This function is only called if createPopupMenu(const QPoint &)
+ returns 0.
+*/
+
+Q3PopupMenu *Q3TextEdit::createPopupMenu()
+{
+ return 0;
+}
+
+/*!
+ \fn Q3TextEdit::zoomIn()
+
+ \overload
+
+ Zooms in on the text by making the base font size one point
+ larger and recalculating all font sizes to be the new size. This
+ does not change the size of any images.
+
+ \sa zoomOut()
+*/
+
+/*!
+ \fn Q3TextEdit::zoomOut()
+
+ \overload
+
+ Zooms out on the text by making the base font size one point
+ smaller and recalculating all font sizes to be the new size. This
+ does not change the size of any images.
+
+ \sa zoomIn()
+*/
+
+
+/*!
+ Zooms in on the text by making the base font size \a range
+ points larger and recalculating all font sizes to be the new size.
+ This does not change the size of any images.
+
+ \sa zoomOut()
+*/
+
+void Q3TextEdit::zoomIn(int range)
+{
+ QFont f(Q3ScrollView::font());
+ f.setPointSize(f.pointSize() + range);
+ setFont(f);
+}
+
+/*!
+ Zooms out on the text by making the base font size \a range points
+ smaller and recalculating all font sizes to be the new size. This
+ does not change the size of any images.
+
+ \sa zoomIn()
+*/
+
+void Q3TextEdit::zoomOut(int range)
+{
+ QFont f(Q3ScrollView::font());
+ f.setPointSize(qMax(1, f.pointSize() - range));
+ setFont(f);
+}
+
+/*!
+ Zooms the text by making the base font size \a size points and
+ recalculating all font sizes to be the new size. This does not
+ change the size of any images.
+*/
+
+void Q3TextEdit::zoomTo(int size)
+{
+ QFont f(Q3ScrollView::font());
+ f.setPointSize(size);
+ setFont(f);
+}
+
+/*!
+ Q3TextEdit is optimized for large amounts text. One of its
+ optimizations is to format only the visible text, formatting the rest
+ on demand, e.g. as the user scrolls, so you don't usually need to
+ call this function.
+
+ In some situations you may want to force the whole text
+ to be formatted. For example, if after calling setText(), you wanted
+ to know the height of the document (using contentsHeight()), you
+ would call this function first.
+*/
+
+void Q3TextEdit::sync()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ QFontMetrics fm(Q3ScrollView::font());
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ } else
+#endif
+ {
+ while (lastFormatted) {
+ lastFormatted->format();
+ lastFormatted = lastFormatted->next();
+ }
+ resizeContents(contentsWidth(), doc->height());
+ }
+ updateScrollBars();
+}
+
+/*!
+ Sets the background color of selection number \a selNum to \a back
+ and specifies whether the text of this selection should be
+ inverted with \a invertText.
+
+ This only works for \a selNum > 0. The default selection (\a
+ selNum == 0) gets its attributes from the text edit's
+ palette().
+*/
+
+void Q3TextEdit::setSelectionAttributes(int selNum, const QColor &back, bool invertText)
+{
+ if (selNum < 1)
+ return;
+ if (selNum > doc->numSelections())
+ doc->addSelection(selNum);
+ doc->setSelectionColor(selNum, back);
+ if (invertText)
+ doc->setSelectionTextColor(selNum, palette().color(QPalette::HighlightedText));
+}
+
+/*!
+ \reimp
+*/
+void Q3TextEdit::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow() && scrollTimer)
+ scrollTimer->stop();
+ if (!palette().isEqual(QPalette::Active, QPalette::Inactive))
+ updateContents();
+ }
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode && (ev->type() == QEvent::ApplicationFontChange
+ || ev->type() == QEvent::FontChange)) {
+ QFont f = font();
+ if (f.kerning())
+ f.setKerning(false);
+
+ setFont(f);
+
+ Q3ScrollView::setFont(f);
+ doc->setDefaultFormat(f, doc->formatCollection()->defaultFormat()->color());
+ // recalculate the max string width
+ QFontMetrics fm(f);
+ int i, sw;
+ d->od->maxLineWidth = 0;
+ for (i = 0; i < d->od->numLines; i++) {
+ sw = fm.width(d->od->lines[LOGOFFSET(i)]);
+ if (d->od->maxLineWidth < sw)
+ d->od->maxLineWidth = sw;
+ }
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ return;
+ }
+#endif
+
+ Q3ScrollView::changeEvent(ev);
+
+ if (textFormat() == Qt::PlainText) {
+ if (ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange
+ || ev->type() == QEvent::EnabledChange) {
+ Q3TextFormat *f = doc->formatCollection()->defaultFormat();
+ f->setColor(palette().text().color());
+ updateContents();
+ }
+ }
+
+ if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) {
+ QFont f = font();
+ if (f.kerning())
+ f.setKerning(false);
+ doc->setMinimumWidth(-1);
+ doc->setDefaultFormat(f, doc->formatCollection()->defaultFormat()->color());
+ lastFormatted = doc->firstParagraph();
+ formatMore();
+ repaintChanged();
+ }
+}
+
+void Q3TextEdit::setReadOnly(bool b)
+{
+ if (readonly == b)
+ return;
+ readonly = b;
+ d->cursorBlinkActive = !b;
+#ifndef QT_NO_CURSOR
+ if (readonly)
+ viewport()->setCursor(Qt::ArrowCursor);
+ else
+ viewport()->setCursor(Qt::IBeamCursor);
+ setInputMethodEnabled(!readonly);
+#endif
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+/*!
+ Scrolls to the bottom of the document and does formatting if
+ required.
+*/
+
+void Q3TextEdit::scrollToBottom()
+{
+ sync();
+ setContentsPos(contentsX(), contentsHeight() - visibleHeight());
+}
+
+/*!
+ Returns the rectangle of the paragraph \a para in contents
+ coordinates, or an invalid rectangle if \a para is out of range.
+*/
+
+QRect Q3TextEdit::paragraphRect(int para) const
+{
+ Q3TextEdit *that = (Q3TextEdit *)this;
+ that->sync();
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return QRect(-1, -1, -1, -1);
+ return p->rect();
+}
+
+/*!
+ Returns the paragraph which is at position \a pos (in contents
+ coordinates).
+*/
+
+int Q3TextEdit::paragraphAt(const QPoint &pos) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ QFontMetrics fm(Q3ScrollView::font());
+ int parag = pos.y() / fm.lineSpacing();
+ if (parag <= d->od->numLines)
+ return parag;
+ else
+ return 0;
+ }
+#endif
+ Q3TextCursor c(doc);
+ c.place(pos, doc->firstParagraph());
+ if (c.paragraph())
+ return c.paragraph()->paragId();
+ return -1; // should never happen..
+}
+
+/*!
+ Returns the index of the character (relative to its paragraph) at
+ position \a pos (in contents coordinates). If \a para is not 0,
+ \c{*}\a{para} is set to the character's paragraph.
+*/
+
+int Q3TextEdit::charAt(const QPoint &pos, int *para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ int par = paragraphAt(pos);
+ if (para)
+ *para = par;
+ return optimCharIndex(d->od->lines[LOGOFFSET(par)], pos.x());
+ }
+#endif
+ Q3TextCursor c(doc);
+ c.place(pos, doc->firstParagraph());
+ if (c.paragraph()) {
+ if (para)
+ *para = c.paragraph()->paragId();
+ return c.index();
+ }
+ return -1; // should never happen..
+}
+
+/*!
+ Sets the background color of the paragraph \a para to \a bg.
+*/
+
+void Q3TextEdit::setParagraphBackgroundColor(int para, const QColor &bg)
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+ p->setBackgroundColor(bg);
+ repaintChanged();
+}
+
+/*!
+ Clears the background color of the paragraph \a para, so that the
+ default color is used again.
+*/
+
+void Q3TextEdit::clearParagraphBackground(int para)
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+ p->clearBackgroundColor();
+ repaintChanged();
+}
+
+/*!
+ Returns the background color of the paragraph \a para or an
+ invalid color if \a para is out of range or the paragraph has no
+ background set
+*/
+
+QColor Q3TextEdit::paragraphBackgroundColor(int para) const
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return QColor();
+ QColor *c = p->backgroundColor();
+ if (c)
+ return *c;
+ return QColor();
+}
+
+/*!
+ \property Q3TextEdit::undoRedoEnabled
+ \brief whether undo/redo is enabled
+
+ When changing this property, the undo/redo history is cleared.
+
+ The default is true.
+*/
+
+void Q3TextEdit::setUndoRedoEnabled(bool b)
+{
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ undoEnabled = b;
+}
+
+bool Q3TextEdit::isUndoRedoEnabled() const
+{
+ return undoEnabled;
+}
+
+/*!
+ Returns true if undo is available; otherwise returns false.
+*/
+
+bool Q3TextEdit::isUndoAvailable() const
+{
+ return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid());
+}
+
+/*!
+ Returns true if redo is available; otherwise returns false.
+*/
+
+bool Q3TextEdit::isRedoAvailable() const
+{
+ return undoEnabled && doc->commands()->isRedoAvailable();
+}
+
+void Q3TextEdit::ensureFormatted(Q3TextParagraph *p)
+{
+ while (!p->isValid()) {
+ if (!lastFormatted)
+ return;
+ formatMore();
+ }
+}
+
+/*! \internal */
+void Q3TextEdit::updateCursor(const QPoint & pos)
+{
+ if (isReadOnly() && linksEnabled()) {
+ Q3TextCursor c = *cursor;
+ placeCursor(pos, &c, true);
+
+#ifndef QT_NO_NETWORKPROTOCOL
+ bool insideParagRect = true;
+ if (c.paragraph() == doc->lastParagraph()
+ && c.paragraph()->rect().y() + c.paragraph()->rect().height() < pos.y())
+ insideParagRect = false;
+ if (insideParagRect && c.paragraph() && c.paragraph()->at(c.index()) &&
+ c.paragraph()->at(c.index())->isAnchor()) {
+ if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()
+ && c.index() < c.paragraph()->length() - 1)
+ onLink = c.paragraph()->at(c.index())->anchorHref();
+ else
+ onLink.clear();
+
+ if (!c.paragraph()->at(c.index())->anchorName().isEmpty()
+ && c.index() < c.paragraph()->length() - 1)
+ d->onName = c.paragraph()->at(c.index())->anchorName();
+ else
+ d->onName.clear();
+
+ if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()) {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(onLink.isEmpty() ? Qt::ArrowCursor : Qt::PointingHandCursor);
+#endif
+ QUrl u = QUrl(doc->context()).resolved(onLink);
+ emitHighlighted(u.toString(QUrl::None));
+ }
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ onLink.clear();
+ emitHighlighted(QString());
+ }
+#endif
+ }
+}
+
+/*!
+ Places the cursor \a c at the character which is closest to position
+ \a pos (in contents coordinates). If \a c is 0, the default text
+ cursor is used.
+
+ \sa setCursorPosition()
+*/
+void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c)
+{
+ placeCursor(pos, c, false);
+}
+
+/*! \internal */
+void Q3TextEdit::clipboardChanged()
+{
+#ifndef QT_NO_CLIPBOARD
+ // don't listen to selection changes
+ disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+#endif
+ selectAll(false);
+}
+
+/*! \property Q3TextEdit::tabChangesFocus
+ \brief whether TAB changes focus or is accepted as input
+
+ In some occasions text edits should not allow the user to input
+ tabulators or change indentation using the TAB key, as this breaks
+ the focus chain. The default is false.
+
+*/
+
+void Q3TextEdit::setTabChangesFocus(bool b)
+{
+ d->tabChangesFocus = b;
+}
+
+bool Q3TextEdit::tabChangesFocus() const
+{
+ return d->tabChangesFocus;
+}
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+/* Implementation of optimized Qt::LogText mode follows */
+
+static void qSwap(int * a, int * b)
+{
+ if (!a || !b)
+ return;
+ int tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/*! \internal */
+bool Q3TextEdit::checkOptimMode()
+{
+ bool oldMode = d->optimMode;
+ if (textFormat() == Qt::LogText) {
+ d->optimMode = true;
+ setReadOnly(true);
+ } else {
+ d->optimMode = false;
+ }
+
+ // when changing mode - try to keep selections and text
+ if (oldMode != d->optimMode) {
+ if (d->optimMode) {
+ d->od = new Q3TextEditOptimPrivate;
+ connect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll()));
+ disconnect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int)));
+ disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone()));
+ disconnect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore()));
+ optimSetText(doc->originalText());
+ doc->clear(true);
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ } else {
+ disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll()));
+ connect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int)));
+ connect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone()));
+ connect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore()));
+ setText(optimText());
+ delete d->od;
+ d->od = 0;
+ }
+ }
+ return d->optimMode;
+}
+
+/*! \internal */
+QString Q3TextEdit::optimText() const
+{
+ QString str, tmp;
+
+ if (d->od->len == 0)
+ return str;
+
+ // concatenate all strings
+ int i;
+ int offset;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ Q3TextEditOptimPrivate::Tag * ftag = 0;
+ for (i = 0; i < d->od->numLines; i++) {
+ if (d->od->lines[LOGOFFSET(i)].isEmpty()) { // CR lines are empty
+ str += QLatin1Char('\n');
+ } else {
+ tmp = d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n');
+ // inject the tags for this line
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(i))) != d->od->tagIndex.constEnd())
+ ftag = it.value();
+ offset = 0;
+ while (ftag && ftag->line == i) {
+ tmp.insert(ftag->index + offset, QLatin1Char('<') + ftag->tag + QLatin1Char('>'));
+ offset += ftag->tag.length() + 2; // 2 -> the '<' and '>' chars
+ ftag = ftag->next;
+ }
+ str += tmp;
+ }
+ }
+ return str;
+}
+
+/*! \internal */
+void Q3TextEdit::optimSetText(const QString &str)
+{
+ optimRemoveSelection();
+// this is just too slow - but may have to go in due to compatibility reasons
+// if (str == optimText())
+// return;
+ d->od->numLines = 0;
+ d->od->lines.clear();
+ d->od->maxLineWidth = 0;
+ d->od->len = 0;
+ d->od->clearTags();
+ QFontMetrics fm(Q3ScrollView::font());
+ if (!(str.isEmpty() || str.isNull() || d->maxLogLines == 0)) {
+ QStringList strl = str.split(QLatin1Char('\n'));
+ int lWidth = 0;
+ for (QStringList::Iterator it = strl.begin(); it != strl.end(); ++it) {
+ optimParseTags(&*it);
+ optimCheckLimit(*it);
+ lWidth = fm.width(*it);
+ if (lWidth > d->od->maxLineWidth)
+ d->od->maxLineWidth = lWidth;
+ }
+ }
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ repaintContents();
+ emit textChanged();
+}
+
+/*! \internal
+
+ Append \a tag to the tag list.
+*/
+Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimAppendTag(int index, const QString & tag)
+{
+ Q3TextEditOptimPrivate::Tag * t = new Q3TextEditOptimPrivate::Tag, * tmp;
+
+ if (d->od->tags == 0)
+ d->od->tags = t;
+ t->bold = t->italic = t->underline = false;
+ t->line = d->od->numLines;
+ t->index = index;
+ t->tag = tag;
+ t->leftTag = 0;
+ t->parent = 0;
+ t->prev = d->od->lastTag;
+ if (d->od->lastTag)
+ d->od->lastTag->next = t;
+ t->next = 0;
+ d->od->lastTag = t;
+ tmp = d->od->tagIndex[LOGOFFSET(t->line)];
+ if (!tmp || (tmp && tmp->index > t->index)) {
+ d->od->tagIndex.insert(LOGOFFSET(t->line), t);
+ }
+ return t;
+}
+
+/*! \internal
+
+ Insert \a tag in the tag - according to line and index numbers
+*/
+Q3TextEditOptimPrivate::Tag *Q3TextEdit::optimInsertTag(int line, int index, const QString &tag)
+{
+ Q3TextEditOptimPrivate::Tag *t = new Q3TextEditOptimPrivate::Tag, *tmp;
+
+ if (d->od->tags == 0)
+ d->od->tags = t;
+ t->bold = t->italic = t->underline = false;
+ t->line = line;
+ t->index = index;
+ t->tag = tag;
+ t->leftTag = 0;
+ t->parent = 0;
+ t->next = 0;
+ t->prev = 0;
+
+ // find insertion pt. in tag struct.
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) {
+ tmp = *it;
+ if (tmp->index >= index) { // the existing tag may be placed AFTER the one we want to insert
+ tmp = tmp->prev;
+ } else {
+ while (tmp && tmp->next && tmp->next->line == line && tmp->next->index <= index)
+ tmp = tmp->next;
+ }
+ } else {
+ tmp = d->od->tags;
+ while (tmp && tmp->next && tmp->next->line < line)
+ tmp = tmp->next;
+ if (tmp == d->od->tags)
+ tmp = 0;
+ }
+
+ t->prev = tmp;
+ t->next = tmp ? tmp->next : 0;
+ if (t->next)
+ t->next->prev = t;
+ if (tmp)
+ tmp->next = t;
+
+ tmp = d->od->tagIndex[LOGOFFSET(t->line)];
+ if (!tmp || (tmp && tmp->index >= t->index)) {
+ d->od->tagIndex.insert(LOGOFFSET(t->line), t);
+ }
+ return t;
+}
+
+/*! \internal
+
+ Find tags in \a line, remove them from \a line and put them in a
+ structure.
+
+ A tag is delimited by '<' and '>'. The characters '<', '>' and '&'
+ are escaped by using '&lt;', '&gt;' and '&amp;'. Left-tags marks
+ the starting point for formatting, while right-tags mark the ending
+ point. A right-tag is the same as a left-tag, but with a '/'
+ appearing before the tag keyword. E.g a valid left-tag: <b>, and
+ a valid right-tag: </b>. Tags can be nested, but they have to be
+ closed in the same order as they are opened. E.g:
+ <font color=red><font color=blue>blue</font>red</font> - is valid, while:
+ <font color=red><b>bold red</font> just bold</b> - is invalid since the font tag is
+ closed before the bold tag. Note that a tag does not have to be
+ closed: '<font color=blue>Lots of text - and then some..' is perfectly valid for
+ setting all text appearing after the tag to blue. A tag can be used
+ to change the color of a piece of text, or set one of the following
+ formatting attributes: bold, italic and underline. These attributes
+ are set using the <b>, <i> and <u> tags. Example of valid tags:
+ <font color=red>, </font>, <b>, <u>, <i>, </i>.
+ Example of valid text:
+ This is some <font color=red>red text</font>, while this is some <font color=green>green
+ text</font>. <font color=blue><font color=yellow>This is yellow</font>, while this is
+ blue.</font>
+
+ Note that only the color attribute of the HTML font tag is supported.
+
+ Limitations:
+ 1. A tag cannot span several lines.
+ 2. Very limited error checking - mismatching left/right-tags is the
+ only thing that is detected.
+
+*/
+void Q3TextEdit::optimParseTags(QString * line, int lineNo, int indexOffset)
+{
+ int len = line->length();
+ int i, startIndex = -1, endIndex = -1, escIndex = -1;
+ int state = 0; // 0 = outside tag, 1 = inside tag
+ bool tagOpen, tagClose;
+ int bold = 0, italic = 0, underline = 0;
+ QString tagStr;
+ QStack<Q3TextEditOptimPrivate::Tag *> tagStack;
+
+ for (i = 0; i < len; i++) {
+ tagOpen = (*line)[i] == QLatin1Char('<');
+ tagClose = (*line)[i] == QLatin1Char('>');
+
+ // handle '&lt;' and '&gt;' and '&amp;'
+ if ((*line)[i] == QLatin1Char('&')) {
+ escIndex = i;
+ continue;
+ } else if (escIndex != -1 && (*line)[i] == QLatin1Char(';')) {
+ QString esc = line->mid(escIndex, i - escIndex + 1);
+ QString c;
+ if (esc == QLatin1String("&lt;"))
+ c = QLatin1Char('<');
+ else if (esc == QLatin1String("&gt;"))
+ c = QLatin1Char('>');
+ else if (esc == QLatin1String("&amp;"))
+ c = QLatin1Char('&');
+ line->replace(escIndex, i - escIndex + 1, c);
+ len = line->length();
+ i -= i-escIndex;
+ escIndex = -1;
+ continue;
+ }
+
+ if (state == 0 && tagOpen) {
+ state = 1;
+ startIndex = i;
+ continue;
+ }
+ if (state == 1 && tagClose) {
+ state = 0;
+ endIndex = i;
+ if (!tagStr.isEmpty()) {
+ Q3TextEditOptimPrivate::Tag * tag, * cur, * tmp;
+ bool format = true;
+
+ if (tagStr == QLatin1String("b"))
+ bold++;
+ else if (tagStr == QLatin1String("/b"))
+ bold--;
+ else if (tagStr == QLatin1String("i"))
+ italic++;
+ else if (tagStr == QLatin1String("/i"))
+ italic--;
+ else if (tagStr == QLatin1String("u"))
+ underline++;
+ else if (tagStr == QLatin1String("/u"))
+ underline--;
+ else
+ format = false;
+ if (lineNo > -1)
+ tag = optimInsertTag(lineNo, startIndex + indexOffset, tagStr);
+ else
+ tag = optimAppendTag(startIndex, tagStr);
+ // everything that is not a b, u or i tag is considered
+ // to be a color tag.
+ tag->type = format ? Q3TextEditOptimPrivate::Format
+ : Q3TextEditOptimPrivate::Color;
+ if (tagStr[0] == QLatin1Char('/')) {
+ // this is a right-tag - search for the left-tag
+ // and possible parent tag
+ cur = tag->prev;
+ if (!cur) {
+ qWarning("Q3TextEdit::optimParseTags: no left-tag for '<%s>' in line %d.",
+ tag->tag.latin1(), tag->line + 1);
+ return; // something is wrong - give up
+ }
+ while (cur) {
+ if (cur->leftTag) { // push right-tags encountered
+ tagStack.push(cur);
+ } else {
+ tmp = tagStack.isEmpty() ? 0 : tagStack.pop();
+ if (!tmp) {
+ if ((QString(QLatin1Char('/') + cur->tag) == tag->tag) ||
+ (tag->tag == QLatin1String("/font") && cur->tag.left(4) == QLatin1String("font"))) {
+ // set up the left and parent of this tag
+ tag->leftTag = cur;
+ tmp = cur->prev;
+ if (tmp && tmp->parent) {
+ tag->parent = tmp->parent;
+ } else if (tmp && !tmp->leftTag) {
+ tag->parent = tmp;
+ }
+ break;
+ } else if (!cur->leftTag) {
+ qWarning("Q3TextEdit::optimParseTags: mismatching %s-tag for '<%s>' in line %d.",
+ qPrintable(QString(cur->tag[0] == QLatin1Char('/') ? QLatin1String("left") : QLatin1String("right"))),
+ cur->tag.latin1(), cur->line + 1);
+ return; // something is amiss - give up
+ }
+ }
+ }
+ cur = cur->prev;
+ }
+ } else {
+ tag->bold = bold > 0;
+ tag->italic = italic > 0;
+ tag->underline = underline > 0;
+ tmp = tag->prev;
+ while (tmp && tmp->leftTag) {
+ tmp = tmp->leftTag->parent;
+ }
+ if (tmp) {
+ tag->bold |= tmp->bold;
+ tag->italic |= tmp->italic;
+ tag->underline |= tmp->underline;
+ }
+ }
+ }
+ if (startIndex != -1) {
+ int l = (endIndex == -1) ?
+ line->length() - startIndex : endIndex - startIndex;
+ line->remove(startIndex, l+1);
+ len = line->length();
+ i -= l+1;
+ }
+ tagStr = QLatin1String("");
+ continue;
+ }
+
+ if (state == 1) {
+ tagStr += (*line)[i];
+ }
+ }
+}
+
+// calculate the width of a string in pixels inc. tabs
+static int qStrWidth(const QString& str, int tabWidth, const QFontMetrics& fm)
+{
+ int tabs = str.count(QLatin1Char('\t'));
+
+ if (!tabs)
+ return fm.width(str);
+
+ int newIdx = 0;
+ int lastIdx = 0;
+ int strWidth = 0;
+ int tn;
+ for (tn = 1; tn <= tabs; ++tn) {
+ newIdx = str.indexOf(QLatin1Char('\t'), newIdx);
+ strWidth += fm.width(str.mid(lastIdx, newIdx - lastIdx));
+ if (strWidth >= tn * tabWidth) {
+ int u = tn;
+ while (strWidth >= u * tabWidth)
+ ++u;
+ strWidth = u * tabWidth;
+ } else {
+ strWidth = tn * tabWidth;
+ }
+ lastIdx = ++newIdx;
+ }
+ if ((int)str.length() > newIdx)
+ strWidth += fm.width(str.mid(newIdx));
+ return strWidth;
+}
+
+bool Q3TextEdit::optimHasBoldMetrics(int line)
+{
+ Q3TextEditOptimPrivate::Tag *t;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ if ((it = d->od->tagIndex.constFind(line)) != d->od->tagIndex.constEnd()) {
+ t = *it;
+ while (t && t->line == line) {
+ if (t->bold)
+ return true;
+ t = t->next;
+ }
+ } else if ((t = optimPreviousLeftTag(line)) && t->bold) {
+ return true;
+ }
+ return false;
+}
+
+/*! \internal
+
+ Append \a str to the current text buffer. Parses each line to find
+ formatting tags.
+*/
+void Q3TextEdit::optimAppend(const QString &str)
+{
+ if (str.isEmpty() || str.isNull() || d->maxLogLines == 0)
+ return;
+
+ QStringList strl = str.split(QLatin1Char('\n'));
+ QStringList::Iterator it = strl.begin();
+
+ QFontMetrics fm(Q3ScrollView::font());
+ int lWidth = 0;
+ for (; it != strl.end(); ++it) {
+ optimParseTags(&*it);
+ optimCheckLimit(*it);
+ if (optimHasBoldMetrics(d->od->numLines-1)) {
+ QFont fnt = Q3ScrollView::font();
+ fnt.setBold(true);
+ fm = QFontMetrics(fnt);
+ }
+ lWidth = qStrWidth(*it, tabStopWidth(), fm) + 4;
+ if (lWidth > d->od->maxLineWidth)
+ d->od->maxLineWidth = lWidth;
+ }
+ bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight();
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ if (scrollToEnd) {
+ updateScrollBars();
+ ensureVisible(contentsX(), contentsHeight(), 0, 0);
+ }
+ // when a max log size is set, the text may not be redrawn because
+ // the size of the viewport may not have changed
+ if (d->maxLogLines > -1)
+ viewport()->update();
+ emit textChanged();
+}
+
+static void qStripTags(QString *line)
+{
+ int len = line->length();
+ int i, startIndex = -1, endIndex = -1, escIndex = -1;
+ int state = 0; // 0 = outside tag, 1 = inside tag
+ bool tagOpen, tagClose;
+
+ for (i = 0; i < len; i++) {
+ tagOpen = (*line)[i] == QLatin1Char('<');
+ tagClose = (*line)[i] == QLatin1Char('>');
+
+ // handle '&lt;' and '&gt;' and '&amp;'
+ if ((*line)[i] == QLatin1Char('&')) {
+ escIndex = i;
+ continue;
+ } else if (escIndex != -1 && (*line)[i] == QLatin1Char(';')) {
+ QString esc = line->mid(escIndex, i - escIndex + 1);
+ QString c;
+ if (esc == QLatin1String("&lt;"))
+ c = QLatin1Char('<');
+ else if (esc == QLatin1String("&gt;"))
+ c = QLatin1Char('>');
+ else if (esc == QLatin1String("&amp;"))
+ c = QLatin1Char('&');
+ line->replace(escIndex, i - escIndex + 1, c);
+ len = line->length();
+ i -= i-escIndex;
+ escIndex = -1;
+ continue;
+ }
+
+ if (state == 0 && tagOpen) {
+ state = 1;
+ startIndex = i;
+ continue;
+ }
+ if (state == 1 && tagClose) {
+ state = 0;
+ endIndex = i;
+ if (startIndex != -1) {
+ int l = (endIndex == -1) ?
+ line->length() - startIndex : endIndex - startIndex;
+ line->remove(startIndex, l+1);
+ len = line->length();
+ i -= l+1;
+ }
+ continue;
+ }
+ }
+}
+
+/*! \internal
+
+ Inserts the text into \a line at index \a index.
+*/
+
+void Q3TextEdit::optimInsert(const QString& text, int line, int index)
+{
+ if (text.isEmpty() || d->maxLogLines == 0)
+ return;
+ if (line < 0)
+ line = 0;
+ if (line > d->od->numLines-1)
+ line = d->od->numLines-1;
+ if (index < 0)
+ index = 0;
+ if (index > d->od->lines[line].length())
+ index = d->od->lines[line].length();
+
+ QStringList strl = text.split(QLatin1Char('\n'));
+ int numNewLines = strl.count() - 1;
+ Q3TextEditOptimPrivate::Tag *tag = 0;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator ii;
+ int x;
+
+ if (numNewLines == 0) {
+ // Case 1. Fast single line case - just inject it!
+ QString stripped = text;
+ qStripTags(&stripped);
+ d->od->lines[LOGOFFSET(line)].insert(index, stripped);
+ // move the tag indices following the insertion pt.
+ if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) {
+ tag = *ii;
+ while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
+ tag = tag->next;
+ while (tag && (LOGOFFSET(tag->line) == line)) {
+ tag->index += stripped.length();
+ tag = tag->next;
+ }
+ }
+ stripped = text;
+ optimParseTags(&stripped, line, index);
+ } else if (numNewLines > 0) {
+ // Case 2. We have at least 1 newline char - split at
+ // insertion pt. and make room for new lines - complex and slow!
+ QString left = d->od->lines[LOGOFFSET(line)].left(index);
+ QString right = d->od->lines[LOGOFFSET(line)].mid(index);
+
+ // rearrange lines for insertion
+ for (x = d->od->numLines - 1; x > line; x--)
+ d->od->lines[x + numNewLines] = d->od->lines[x];
+ d->od->numLines += numNewLines;
+
+ // fix the tag index and the tag line/index numbers - this
+ // might take a while..
+ for (x = line; x < d->od->numLines; x++) {
+ if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) {
+ tag = ii.value();
+ if (LOGOFFSET(tag->line) == line)
+ while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
+ tag = tag->next;
+ }
+ }
+
+ // relabel affected tags with new line numbers and new index
+ // positions
+ while (tag) {
+ if (LOGOFFSET(tag->line) == line)
+ tag->index -= index;
+ tag->line += numNewLines;
+ tag = tag->next;
+ }
+
+ // generate a new tag index
+ d->od->tagIndex.clear();
+ tag = d->od->tags;
+ while (tag) {
+ if (!((ii = d->od->tagIndex.constFind(LOGOFFSET(tag->line))) != d->od->tagIndex.constEnd()))
+ d->od->tagIndex[LOGOFFSET(tag->line)] = tag;
+ tag = tag->next;
+ }
+
+ // update the tag indices on the spliced line - needs to be done before new tags are added
+ QString stripped = strl[strl.count() - 1];
+ qStripTags(&stripped);
+ if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line + numNewLines))) != d->od->tagIndex.constEnd()) {
+ tag = *ii;
+ while (tag && (LOGOFFSET(tag->line) == line + numNewLines)) {
+ tag->index += stripped.length();
+ tag = tag->next;
+ }
+ }
+
+ // inject the new lines
+ QStringList::Iterator it = strl.begin();
+ x = line;
+ int idx;
+ for (; it != strl.end(); ++it) {
+ stripped = *it;
+ qStripTags(&stripped);
+ if (x == line) {
+ stripped = left + stripped;
+ idx = index;
+ } else {
+ idx = 0;
+ }
+ d->od->lines[LOGOFFSET(x)] = stripped;
+ optimParseTags(&*it, x++, idx);
+ }
+ d->od->lines[LOGOFFSET(x - 1)] += right;
+ }
+ // recalculate the pixel width of the longest injected line -
+ QFontMetrics fm(Q3ScrollView::font());
+ int lWidth = 0;
+ for (x = line; x < line + numNewLines; x++) {
+ if (optimHasBoldMetrics(x)) {
+ QFont fnt = Q3ScrollView::font();
+ fnt.setBold(true);
+ fm = QFontMetrics(fnt);
+ }
+ lWidth = fm.width(d->od->lines[x]) + 4;
+ if (lWidth > d->od->maxLineWidth)
+ d->od->maxLineWidth = lWidth;
+ }
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ repaintContents();
+ emit textChanged();
+}
+
+
+/*! \internal
+
+ Returns the first open left-tag appearing before line \a line.
+ */
+Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimPreviousLeftTag(int line)
+{
+ Q3TextEditOptimPrivate::Tag * ftag = 0;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd())
+ ftag = it.value();
+ if (!ftag) {
+ // start searching for an open tag
+ ftag = d->od->tags;
+ while (ftag) {
+ if (ftag->line > line || ftag->next == 0) {
+ if (ftag->line > line)
+ ftag = ftag->prev;
+ break;
+ }
+ ftag = ftag->next;
+ }
+ } else {
+ ftag = ftag->prev;
+ }
+
+ if (ftag) {
+ if (ftag && ftag->parent) // use the open parent tag
+ ftag = ftag->parent;
+ else if (ftag && ftag->leftTag) // this is a right-tag with no parent
+ ftag = 0;
+ }
+ return ftag;
+}
+
+/*! \internal
+
+ Set the format for the string starting at index \a start and ending
+ at \a end according to \a tag. If \a tag is a Format tag, find the
+ first open color tag appearing before \a tag and use that tag to
+ color the string.
+*/
+void Q3TextEdit::optimSetTextFormat(Q3TextDocument * td, Q3TextCursor * cur,
+ Q3TextFormat * f, int start, int end,
+ Q3TextEditOptimPrivate::Tag * tag)
+{
+ int formatFlags = Q3TextFormat::Bold | Q3TextFormat::Italic |
+ Q3TextFormat::Underline;
+ cur->setIndex(start);
+ td->setSelectionStart(0, *cur);
+ cur->setIndex(end);
+ td->setSelectionEnd(0, *cur);
+ Q3StyleSheetItem * ssItem = styleSheet()->item(tag->tag);
+ if (!ssItem || tag->type == Q3TextEditOptimPrivate::Format) {
+ f->setBold(tag->bold);
+ f->setItalic(tag->italic);
+ f->setUnderline(tag->underline);
+ if (tag->type == Q3TextEditOptimPrivate::Format) {
+ // check to see if there are any open color tags prior to
+ // this format tag
+ tag = tag->prev;
+ while (tag && (tag->type == Q3TextEditOptimPrivate::Format ||
+ tag->leftTag)) {
+ tag = tag->leftTag ? tag->parent : tag->prev;
+ }
+ }
+ if (tag) {
+ QString col = tag->tag.simplified();
+ if (col.startsWith(QLatin1String("font color"))) {
+ int i = col.indexOf(QLatin1Char('='), 10);
+ col = col.mid(i + 1).simplified();
+ if (col[0] == QLatin1Char('\"'))
+ col = col.mid(1, col.length() - 2);
+ }
+ QColor color = QColor(col);
+ if (color.isValid()) {
+ formatFlags |= Q3TextFormat::Color;
+ f->setColor(color);
+ }
+ }
+ } else { // use the stylesheet tag definition
+ if (ssItem->color().isValid()) {
+ formatFlags |= Q3TextFormat::Color;
+ f->setColor(ssItem->color());
+ }
+ f->setBold(ssItem->fontWeight() == QFont::Bold);
+ f->setItalic(ssItem->fontItalic());
+ f->setUnderline(ssItem->fontUnderline());
+ }
+ td->setFormat(0, f, formatFlags);
+ td->removeSelection(0);
+}
+
+/*! \internal */
+void Q3TextEdit::optimDrawContents(QPainter * p, int clipx, int clipy,
+ int clipw, int cliph)
+{
+ QFontMetrics fm(Q3ScrollView::font());
+ int startLine = clipy / fm.lineSpacing();
+
+ // we always have to fetch at least two lines for drawing because the
+ // painter may be translated so that parts of two lines cover the area
+ // of a single line
+ int nLines = (cliph / fm.lineSpacing()) + 2;
+ int endLine = startLine + nLines;
+
+ if (startLine >= d->od->numLines)
+ return;
+ if ((startLine + nLines) > d->od->numLines)
+ nLines = d->od->numLines - startLine;
+
+ int i = 0;
+ QString str;
+ for (i = startLine; i < (startLine + nLines); i++)
+ str.append(d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n'));
+
+ Q3TextDocument * td = new Q3TextDocument(0);
+ td->setDefaultFormat(Q3ScrollView::font(), QColor());
+ td->setPlainText(str);
+ td->setFormatter(new Q3TextFormatterBreakWords); // deleted by QTextDoc
+ td->formatter()->setWrapEnabled(false);
+ td->setTabStops(doc->tabStopWidth());
+
+ // get the current text color from the current format
+ td->selectAll(Q3TextDocument::Standard);
+ Q3TextFormat f;
+ f.setColor(palette().text().color());
+ f.setFont(Q3ScrollView::font());
+ td->setFormat(Q3TextDocument::Standard, &f,
+ Q3TextFormat::Color | Q3TextFormat::Font);
+ td->removeSelection(Q3TextDocument::Standard);
+
+ // add tag formatting
+ if (d->od->tags) {
+ int i = startLine;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ Q3TextEditOptimPrivate::Tag * tag = 0, * tmp = 0;
+ Q3TextCursor cur(td);
+ // Step 1 - find previous left-tag
+ tmp = optimPreviousLeftTag(i);
+ for (; i < startLine + nLines; i++) {
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(i))) != d->od->tagIndex.constEnd())
+ tag = it.value();
+ // Step 2 - iterate over tags on the current line
+ int lastIndex = 0;
+ while (tag && tag->line == i) {
+ tmp = 0;
+ if (tag->prev && !tag->prev->leftTag) {
+ tmp = tag->prev;
+ } else if (tag->prev && tag->prev->parent) {
+ tmp = tag->prev->parent;
+ }
+ if ((tag->index - lastIndex) > 0 && tmp) {
+ optimSetTextFormat(td, &cur, &f, lastIndex, tag->index, tmp);
+ }
+ lastIndex = tag->index;
+ tmp = tag;
+ tag = tag->next;
+ }
+ // Step 3 - color last part of the line - if necessary
+ if (tmp && tmp->parent)
+ tmp = tmp->parent;
+ if ((cur.paragraph()->length()-1 - lastIndex) > 0 && tmp && !tmp->leftTag) {
+ optimSetTextFormat(td, &cur, &f, lastIndex,
+ cur.paragraph()->length() - 1, tmp);
+ }
+ cur.setParagraph(cur.paragraph()->next());
+ }
+ // useful debug info
+ //
+// tag = d->od->tags;
+// qWarning("###");
+// while (tag) {
+// qWarning("Tag: %p, parent: %09p, leftTag: %09p, Name: %-15s, ParentName: %s, %d%d%d", tag,
+// tag->parent, tag->leftTag, tag->tag.latin1(), tag->parent ? tag->parent->tag.latin1():"<none>",
+// tag->bold, tag->italic, tag->underline);
+// tag = tag->next;
+// }
+ }
+
+ // if there is a selection, make sure that the selection in the
+ // part we need to redraw is set correctly
+ if (optimHasSelection()) {
+ Q3TextCursor c1(td);
+ Q3TextCursor c2(td);
+ int selStart = d->od->selStart.line;
+ int idxStart = d->od->selStart.index;
+ int selEnd = d->od->selEnd.line;
+ int idxEnd = d->od->selEnd.index;
+ if (selEnd < selStart) {
+ qSwap(&selStart, &selEnd);
+ qSwap(&idxStart, &idxEnd);
+ }
+ if (selEnd > d->od->numLines-1) {
+ selEnd = d->od->numLines-1;
+ }
+ if (startLine <= selStart && endLine >= selEnd) {
+ // case 1: area to paint covers entire selection
+ int paragS = selStart - startLine;
+ int paragE = paragS + (selEnd - selStart);
+ Q3TextParagraph * parag = td->paragAt(paragS);
+ if (parag) {
+ c1.setParagraph(parag);
+ if (td->text(paragS).length() >= idxStart)
+ c1.setIndex(idxStart);
+ }
+ parag = td->paragAt(paragE);
+ if (parag) {
+ c2.setParagraph(parag);
+ if (td->text(paragE).length() >= idxEnd)
+ c2.setIndex(idxEnd);
+ }
+ } else if (startLine > selStart && endLine < selEnd) {
+ // case 2: area to paint is all part of the selection
+ td->selectAll(Q3TextDocument::Standard);
+ } else if (startLine > selStart && endLine >= selEnd &&
+ startLine <= selEnd) {
+ // case 3: area to paint starts inside a selection, ends past it
+ c1.setParagraph(td->firstParagraph());
+ c1.setIndex(0);
+ int paragE = selEnd - startLine;
+ Q3TextParagraph * parag = td->paragAt(paragE);
+ if (parag) {
+ c2.setParagraph(parag);
+ if (td->text(paragE).length() >= idxEnd)
+ c2.setIndex(idxEnd);
+ }
+ } else if (startLine <= selStart && endLine < selEnd &&
+ endLine > selStart) {
+ // case 4: area to paint starts before a selection, ends inside it
+ int paragS = selStart - startLine;
+ Q3TextParagraph * parag = td->paragAt(paragS);
+ if (parag) {
+ c1.setParagraph(parag);
+ c1.setIndex(idxStart);
+ }
+ c2.setParagraph(td->lastParagraph());
+ c2.setIndex(td->lastParagraph()->string()->toString().length() - 1);
+
+ }
+ // previously selected?
+ if (!td->hasSelection(Q3TextDocument::Standard)) {
+ td->setSelectionStart(Q3TextDocument::Standard, c1);
+ td->setSelectionEnd(Q3TextDocument::Standard, c2);
+ }
+ }
+ td->doLayout(p, contentsWidth());
+
+ // have to align the painter so that partly visible lines are
+ // drawn at the correct position within the area that needs to be
+ // painted
+ int offset = clipy % fm.lineSpacing() + 2;
+ QRect r(clipx, 0, clipw, cliph + offset);
+ p->translate(0, clipy - offset);
+ td->draw(p, r.x(), r.y(), r.width(), r.height(), palette());
+ p->translate(0, -(clipy - offset));
+ delete td;
+}
+
+/*! \internal */
+void Q3TextEdit::optimMousePressEvent(QMouseEvent * e)
+{
+ if (e->button() != Qt::LeftButton)
+ return;
+
+ QFontMetrics fm(Q3ScrollView::font());
+ mousePressed = true;
+ mousePos = e->pos();
+ d->od->selStart.line = e->y() / fm.lineSpacing();
+ if (d->od->selStart.line > d->od->numLines-1) {
+ d->od->selStart.line = d->od->numLines-1;
+ d->od->selStart.index = d->od->lines[LOGOFFSET(d->od->numLines-1)].length();
+ } else {
+ QString str = d->od->lines[LOGOFFSET(d->od->selStart.line)];
+ d->od->selStart.index = optimCharIndex(str, mousePos.x());
+ }
+ d->od->selEnd.line = d->od->selStart.line;
+ d->od->selEnd.index = d->od->selStart.index;
+ oldMousePos = e->pos();
+ repaintContents();
+}
+
+/*! \internal */
+void Q3TextEdit::optimMouseReleaseEvent(QMouseEvent * e)
+{
+ if (e->button() != Qt::LeftButton)
+ return;
+
+ if (scrollTimer->isActive())
+ scrollTimer->stop();
+ if (!inDoubleClick) {
+ QFontMetrics fm(Q3ScrollView::font());
+ d->od->selEnd.line = e->y() / fm.lineSpacing();
+ if (d->od->selEnd.line > d->od->numLines-1) {
+ d->od->selEnd.line = d->od->numLines-1;
+ }
+ QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)];
+ mousePos = e->pos();
+ d->od->selEnd.index = optimCharIndex(str, mousePos.x());
+ if (d->od->selEnd.line < d->od->selStart.line) {
+ qSwap(&d->od->selStart.line, &d->od->selEnd.line);
+ qSwap(&d->od->selStart.index, &d->od->selEnd.index);
+ } else if (d->od->selStart.line == d->od->selEnd.line &&
+ d->od->selStart.index > d->od->selEnd.index) {
+ qSwap(&d->od->selStart.index, &d->od->selEnd.index);
+ }
+ oldMousePos = e->pos();
+ repaintContents();
+ }
+ if (mousePressed) {
+ mousePressed = false;
+ copyToClipboard();
+ }
+
+ inDoubleClick = false;
+ emit copyAvailable(optimHasSelection());
+ emit selectionChanged();
+}
+
+/*! \internal */
+void Q3TextEdit::optimMouseMoveEvent(QMouseEvent * e)
+{
+ mousePos = e->pos();
+ optimDoAutoScroll();
+ oldMousePos = mousePos;
+}
+
+/*! \internal */
+void Q3TextEdit::optimDoAutoScroll()
+{
+ if (!mousePressed)
+ return;
+
+ QFontMetrics fm(Q3ScrollView::font());
+ QPoint pos(mapFromGlobal(QCursor::pos()));
+ bool doScroll = false;
+ int xx = contentsX() + pos.x();
+ int yy = contentsY() + pos.y();
+
+ // find out how much we have to scroll in either dir.
+ if (pos.x() < 0 || pos.x() > viewport()->width() ||
+ pos.y() < 0 || pos.y() > viewport()->height()) {
+ int my = yy;
+ if (pos.x() < 0)
+ xx = contentsX() - fm.width(QLatin1Char('w'));
+ else if (pos.x() > viewport()->width())
+ xx = contentsX() + viewport()->width() + fm.width(QLatin1Char('w'));
+
+ if (pos.y() < 0) {
+ my = contentsY() - 1;
+ yy = (my / fm.lineSpacing()) * fm.lineSpacing() + 1;
+ } else if (pos.y() > viewport()->height()) {
+ my = contentsY() + viewport()->height() + 1;
+ yy = (my / fm.lineSpacing() + 1) * fm.lineSpacing() - 1;
+ }
+ d->od->selEnd.line = my / fm.lineSpacing();
+ mousePos.setX(xx);
+ mousePos.setY(my);
+ doScroll = true;
+ } else {
+ d->od->selEnd.line = mousePos.y() / fm.lineSpacing();
+ }
+
+ if (d->od->selEnd.line < 0) {
+ d->od->selEnd.line = 0;
+ } else if (d->od->selEnd.line > d->od->numLines-1) {
+ d->od->selEnd.line = d->od->numLines-1;
+ }
+
+ QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)];
+ d->od->selEnd.index = optimCharIndex(str, mousePos.x());
+
+ // have to have a valid index before generating a paint event
+ if (doScroll)
+ ensureVisible(xx, yy, 1, 1);
+
+ // if the text document is smaller than the height of the viewport
+ // - redraw the whole thing otherwise calculate the rect that
+ // needs drawing.
+ if (d->od->numLines * fm.lineSpacing() < viewport()->height()) {
+ repaintContents(contentsX(), contentsY(), width(), height());
+ } else {
+ int h = QABS(mousePos.y() - oldMousePos.y()) + fm.lineSpacing() * 2;
+ int y;
+ if (oldMousePos.y() < mousePos.y()) {
+ y = oldMousePos.y() - fm.lineSpacing();
+ } else {
+ // expand paint area for a fully selected line
+ h += fm.lineSpacing();
+ y = mousePos.y() - fm.lineSpacing()*2;
+ }
+ if (y < 0)
+ y = 0;
+ repaintContents(contentsX(), y, width(), h);
+ }
+
+ if ((!scrollTimer->isActive() && pos.y() < 0) || pos.y() > height())
+ scrollTimer->start(100, false);
+ else if (scrollTimer->isActive() && pos.y() >= 0 && pos.y() <= height())
+ scrollTimer->stop();
+}
+
+/*! \internal
+
+ Returns the index of the character in the string \a str that is
+ currently under the mouse pointer.
+*/
+int Q3TextEdit::optimCharIndex(const QString &str, int mx) const
+{
+ QFontMetrics fm(Q3ScrollView::font());
+ int i = 0;
+ int dd, dist = 10000000;
+ int curpos = 0;
+ int strWidth;
+ mx = mx - 4; // ### get the real margin from somewhere
+
+ if (!str.contains(QLatin1Char('\t')) && mx > fm.width(str))
+ return str.length();
+
+ while (i < str.length()) {
+ strWidth = qStrWidth(str.left(i), tabStopWidth(), fm);
+ dd = strWidth - mx;
+ if (QABS(dd) <= dist) {
+ dist = QABS(dd);
+ if (mx >= strWidth)
+ curpos = i;
+ }
+ ++i;
+ }
+ return curpos;
+}
+
+/*! \internal */
+void Q3TextEdit::optimSelectAll()
+{
+ d->od->selStart.line = d->od->selStart.index = 0;
+ d->od->selEnd.line = d->od->numLines - 1;
+ d->od->selEnd.index = d->od->lines[LOGOFFSET(d->od->selEnd.line)].length();
+
+ repaintContents();
+ emit copyAvailable(optimHasSelection());
+ emit selectionChanged();
+}
+
+/*! \internal */
+void Q3TextEdit::optimRemoveSelection()
+{
+ d->od->selStart.line = d->od->selEnd.line = -1;
+ d->od->selStart.index = d->od->selEnd.index = -1;
+ repaintContents();
+}
+
+/*! \internal */
+void Q3TextEdit::optimSetSelection(int startLine, int startIdx,
+ int endLine, int endIdx)
+{
+ d->od->selStart.line = startLine;
+ d->od->selEnd.line = endLine;
+ d->od->selStart.index = startIdx;
+ d->od->selEnd.index = endIdx;
+}
+
+/*! \internal */
+bool Q3TextEdit::optimHasSelection() const
+{
+ if (d->od->selStart.line != d->od->selEnd.line ||
+ d->od->selStart.index != d->od->selEnd.index)
+ return true;
+ return false;
+}
+
+/*! \internal */
+QString Q3TextEdit::optimSelectedText() const
+{
+ QString str;
+
+ if (!optimHasSelection())
+ return str;
+
+ // concatenate all strings
+ if (d->od->selStart.line == d->od->selEnd.line) {
+ str = d->od->lines[LOGOFFSET(d->od->selEnd.line)].mid(d->od->selStart.index,
+ d->od->selEnd.index - d->od->selStart.index);
+ } else {
+ int i = d->od->selStart.line;
+ str = d->od->lines[LOGOFFSET(i)].right(d->od->lines[LOGOFFSET(i)].length() -
+ d->od->selStart.index) + QLatin1Char('\n');
+ i++;
+ for (; i < d->od->selEnd.line; i++) {
+ if (d->od->lines[LOGOFFSET(i)].isEmpty()) // CR lines are empty
+ str += QLatin1Char('\n');
+ else
+ str += d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n');
+ }
+ str += d->od->lines[LOGOFFSET(d->od->selEnd.line)].left(d->od->selEnd.index);
+ }
+ return str;
+}
+
+/*! \internal */
+bool Q3TextEdit::optimFind(const QString & expr, bool cs, bool /*wo*/,
+ bool fw, int * para, int * index)
+{
+ bool found = false;
+ int parag = para ? *para : d->od->search.line,
+ idx = index ? *index : d->od->search.index, i;
+
+ if (d->od->len == 0)
+ return false;
+
+ for (i = parag; fw ? i < d->od->numLines : i >= 0; fw ? i++ : i--) {
+ idx = fw
+ ? d->od->lines[LOGOFFSET(i)].indexOf(expr, idx,
+ cs ? Qt::CaseSensitive : Qt::CaseInsensitive)
+ : d->od->lines[LOGOFFSET(i)].lastIndexOf(expr, idx,
+ cs ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ if (idx != -1) {
+ found = true;
+ break;
+ } else if (fw)
+ idx = 0;
+ }
+
+ if (found) {
+ if (index)
+ *index = idx;
+ if (para)
+ *para = i;
+ d->od->search.index = idx;
+ d->od->search.line = i;
+ optimSetSelection(i, idx, i, idx + expr.length());
+ QFontMetrics fm(Q3ScrollView::font());
+ int h = fm.lineSpacing();
+ int x = fm.width(d->od->lines[LOGOFFSET(i)].left(idx + expr.length())) + 4;
+ ensureVisible(x, i * h + h / 2, 1, h / 2 + 2);
+ repaintContents(); // could possibly be optimized
+ }
+ return found;
+}
+
+/*! \reimp */
+void Q3TextEdit::polishEvent(QEvent*)
+{
+ // this will ensure that the last line is visible if text have
+ // been added to the widget before it is shown
+ if (d->optimMode)
+ scrollToBottom();
+}
+
+/*!
+ Sets the maximum number of lines a Q3TextEdit can hold in \c
+ Qt::LogText mode to \a limit. If \a limit is -1 (the default), this
+ signifies an unlimited number of lines.
+
+ \warning Never use formatting tags that span more than one line
+ when the maximum log lines is set. When lines are removed from the
+ top of the buffer it could result in an unbalanced tag pair, i.e.
+ the left formatting tag is removed before the right one.
+ */
+void Q3TextEdit::setMaxLogLines(int limit)
+{
+ d->maxLogLines = limit;
+ if (d->maxLogLines < -1)
+ d->maxLogLines = -1;
+ if (d->maxLogLines == -1)
+ d->logOffset = 0;
+}
+
+/*!
+ Returns the maximum number of lines Q3TextEdit can hold in \c
+ Qt::LogText mode. By default the number of lines is unlimited, which
+ is signified by a value of -1.
+ */
+int Q3TextEdit::maxLogLines() const
+{
+ return d->maxLogLines;
+}
+
+/*!
+ Check if the number of lines in the buffer is limited, and uphold
+ that limit when appending new lines.
+ */
+void Q3TextEdit::optimCheckLimit(const QString& str)
+{
+ if (d->maxLogLines > -1 && d->maxLogLines <= d->od->numLines) {
+ // NB! Removing the top line in the buffer will potentially
+ // destroy the structure holding the formatting tags - if line
+ // spanning tags are used.
+ Q3TextEditOptimPrivate::Tag *t = d->od->tags, *tmp, *itr;
+ QList<Q3TextEditOptimPrivate::Tag *> lst;
+ while (t) {
+ t->line -= 1;
+ // unhook the ptr from the tag structure
+ if (((uint) LOGOFFSET(t->line) < (uint) d->logOffset &&
+ (uint) LOGOFFSET(t->line) < (uint) LOGOFFSET(d->od->numLines) &&
+ (uint) LOGOFFSET(d->od->numLines) > (uint) d->logOffset))
+ {
+ if (t->prev)
+ t->prev->next = t->next;
+ if (t->next)
+ t->next->prev = t->prev;
+ if (d->od->tags == t)
+ d->od->tags = t->next;
+ if (d->od->lastTag == t) {
+ if (t->prev)
+ d->od->lastTag = t->prev;
+ else
+ d->od->lastTag = d->od->tags;
+ }
+ tmp = t;
+ t = t->next;
+ lst.append(tmp);
+ delete tmp;
+ } else {
+ t = t->next;
+ }
+ }
+ // Remove all references to the ptrs we just deleted
+ itr = d->od->tags;
+ while (itr) {
+ for (int i = 0; i < lst.size(); ++i) {
+ tmp = lst.at(i);
+ if (itr->parent == tmp)
+ itr->parent = 0;
+ if (itr->leftTag == tmp)
+ itr->leftTag = 0;
+ }
+ itr = itr->next;
+ }
+ // ...in the tag index as well
+ QMap<int, Q3TextEditOptimPrivate::Tag *>::Iterator idx;
+ if ((idx = d->od->tagIndex.find(d->logOffset)) != d->od->tagIndex.end())
+ d->od->tagIndex.erase(idx);
+
+ QMap<int,QString>::Iterator it;
+ if ((it = d->od->lines.find(d->logOffset)) != d->od->lines.end()) {
+ d->od->len -= (*it).length();
+ d->od->lines.erase(it);
+ d->od->numLines--;
+ d->logOffset = LOGOFFSET(1);
+ }
+ }
+ d->od->len += str.length();
+ d->od->lines[LOGOFFSET(d->od->numLines++)] = str;
+}
+
+#endif // QT_TEXTEDIT_OPTIMIZATION
+
+/*!
+ \property Q3TextEdit::autoFormatting
+ \brief the enabled set of auto formatting features
+
+ The value can be any combination of the values in the \c
+ AutoFormattingFlag enum. The default is \c AutoAll. Choose \c AutoNone
+ to disable all automatic formatting.
+
+ Currently, the only automatic formatting feature provided is \c
+ AutoBulletList; future versions of Qt may offer more.
+*/
+
+void Q3TextEdit::setAutoFormatting(AutoFormatting features)
+{
+ d->autoFormatting = features;
+}
+
+Q3TextEdit::AutoFormatting Q3TextEdit::autoFormatting() const
+{
+ return d->autoFormatting;
+}
+
+/*!
+ Returns the QSyntaxHighlighter set on this Q3TextEdit. 0 is
+ returned if no syntax highlighter is set.
+ */
+Q3SyntaxHighlighter * Q3TextEdit::syntaxHighlighter() const
+{
+ if (document()->preProcessor())
+ return ((Q3SyntaxHighlighterInternal *) document()->preProcessor())->highlighter;
+ else
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_TEXTEDIT
diff --git a/src/qt3support/text/q3textedit.h b/src/qt3support/text/q3textedit.h
new file mode 100644
index 0000000..86bfc7f
--- /dev/null
+++ b/src/qt3support/text/q3textedit.h
@@ -0,0 +1,613 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTEDIT_H
+#define Q3TEXTEDIT_H
+
+#include <Qt3Support/q3scrollview.h>
+#include <Qt3Support/q3stylesheet.h>
+#include <Qt3Support/q3mimefactory.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_TEXTEDIT
+// uncomment below to enable optimization mode - also uncomment the
+// optimDoAutoScroll() private slot since moc ignores #ifdefs..
+#define QT_TEXTEDIT_OPTIMIZATION
+
+class QPainter;
+class Q3TextDocument;
+class Q3TextCursor;
+class QKeyEvent;
+class QResizeEvent;
+class QMouseEvent;
+class QTimer;
+class Q3TextString;
+class QTextCommand;
+class Q3TextParagraph;
+class Q3TextFormat;
+class QFont;
+class QColor;
+class Q3TextEdit;
+class QTextBrowser;
+class Q3TextString;
+struct QUndoRedoInfoPrivate;
+class Q3PopupMenu;
+class Q3TextEditPrivate;
+class Q3SyntaxHighlighter;
+class Q3TextDrag;
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+class Q3TextEditOptimPrivate
+{
+public:
+ // Note: no left-tag has any value for leftTag or parent, and
+ // no right-tag has any formatting flags set.
+ enum TagType { Color = 0, Format = 1 };
+ struct Tag {
+ TagType type:2;
+ bool bold:1;
+ bool italic:1;
+ bool underline:1;
+ int line;
+ int index;
+ Tag * leftTag; // ptr to left-tag in a left-right tag pair
+ Tag * parent; // ptr to parent left-tag in a nested tag
+ Tag * prev;
+ Tag * next;
+ QString tag;
+ };
+ Q3TextEditOptimPrivate()
+ {
+ len = numLines = maxLineWidth = 0;
+ selStart.line = selStart.index = -1;
+ selEnd.line = selEnd.index = -1;
+ search.line = search.index = 0;
+ tags = lastTag = 0;
+ }
+ void clearTags()
+ {
+ Tag * itr = tags;
+ while (tags) {
+ itr = tags;
+ tags = tags->next;
+ delete itr;
+ }
+ tags = lastTag = 0;
+ tagIndex.clear();
+ }
+ ~Q3TextEditOptimPrivate()
+ {
+ clearTags();
+ }
+ int len;
+ int numLines;
+ int maxLineWidth;
+ struct Selection {
+ int line;
+ int index;
+ };
+ Selection selStart, selEnd, search;
+ Tag * tags, * lastTag;
+ QMap<int, QString> lines;
+ QMap<int, Tag *> tagIndex;
+};
+#endif
+
+class Q_COMPAT_EXPORT Q3TextEdit : public Q3ScrollView
+{
+ friend class Q3TextBrowser;
+ friend class Q3SyntaxHighlighter;
+
+ Q_OBJECT
+ Q_ENUMS(WordWrap WrapPolicy)
+ Q_FLAGS(AutoFormattingFlag)
+ Q_PROPERTY(Qt::TextFormat textFormat READ textFormat WRITE setTextFormat)
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QBrush paper READ paper WRITE setPaper)
+ Q_PROPERTY(bool linkUnderline READ linkUnderline WRITE setLinkUnderline)
+ Q_PROPERTY(QString documentTitle READ documentTitle)
+ Q_PROPERTY(int length READ length)
+ Q_PROPERTY(WordWrap wordWrap READ wordWrap WRITE setWordWrap)
+ Q_PROPERTY(int wrapColumnOrWidth READ wrapColumnOrWidth WRITE setWrapColumnOrWidth)
+ Q_PROPERTY(WrapPolicy wrapPolicy READ wrapPolicy WRITE setWrapPolicy)
+ Q_PROPERTY(bool hasSelectedText READ hasSelectedText)
+ Q_PROPERTY(QString selectedText READ selectedText)
+ Q_PROPERTY(int undoDepth READ undoDepth WRITE setUndoDepth)
+ Q_PROPERTY(bool overwriteMode READ isOverwriteMode WRITE setOverwriteMode)
+ Q_PROPERTY(bool modified READ isModified WRITE setModified DESIGNABLE false)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(bool undoRedoEnabled READ isUndoRedoEnabled WRITE setUndoRedoEnabled)
+ Q_PROPERTY(int tabStopWidth READ tabStopWidth WRITE setTabStopWidth)
+ Q_PROPERTY(bool tabChangesFocus READ tabChangesFocus WRITE setTabChangesFocus)
+ Q_PROPERTY(AutoFormattingFlag autoFormatting READ autoFormatting WRITE setAutoFormatting)
+
+public:
+ enum WordWrap {
+ NoWrap,
+ WidgetWidth,
+ FixedPixelWidth,
+ FixedColumnWidth
+ };
+
+ enum WrapPolicy {
+ AtWordBoundary,
+ AtWhiteSpace = AtWordBoundary, // AtWhiteSpace is deprecated
+ Anywhere,
+ AtWordOrDocumentBoundary
+ };
+
+ enum AutoFormattingFlag {
+ AutoNone = 0,
+ AutoBulletList = 0x00000001,
+ AutoAll = 0xffffffff
+ };
+
+ Q_DECLARE_FLAGS(AutoFormatting, AutoFormattingFlag)
+
+ enum KeyboardAction {
+ ActionBackspace,
+ ActionDelete,
+ ActionReturn,
+ ActionKill,
+ ActionWordBackspace,
+ ActionWordDelete
+ };
+
+ enum CursorAction {
+ MoveBackward,
+ MoveForward,
+ MoveWordBackward,
+ MoveWordForward,
+ MoveUp,
+ MoveDown,
+ MoveLineStart,
+ MoveLineEnd,
+ MoveHome,
+ MoveEnd,
+ MovePgUp,
+ MovePgDown
+ };
+
+ enum VerticalAlignment {
+ AlignNormal,
+ AlignSuperScript,
+ AlignSubScript
+ };
+
+ enum TextInsertionFlags {
+ RedoIndentation = 0x0001,
+ CheckNewLines = 0x0002,
+ RemoveSelected = 0x0004
+ };
+
+ Q3TextEdit(const QString& text, const QString& context = QString(),
+ QWidget* parent=0, const char* name=0);
+ Q3TextEdit(QWidget* parent=0, const char* name=0);
+ virtual ~Q3TextEdit();
+
+ QString text() const;
+ QString text(int para) const;
+ Qt::TextFormat textFormat() const;
+ QString context() const;
+ QString documentTitle() const;
+
+ void getSelection(int *paraFrom, int *indexFrom,
+ int *paraTo, int *indexTo, int selNum = 0) const;
+ virtual bool find(const QString &expr, bool cs, bool wo, bool forward = true,
+ int *para = 0, int *index = 0);
+
+ int paragraphs() const;
+ int lines() const;
+ int linesOfParagraph(int para) const;
+ int lineOfChar(int para, int chr);
+ int length() const;
+ QRect paragraphRect(int para) const;
+ int paragraphAt(const QPoint &pos) const;
+ int charAt(const QPoint &pos, int *para) const;
+ int paragraphLength(int para) const;
+
+ Q3StyleSheet* styleSheet() const;
+#ifndef QT_NO_MIME
+ Q3MimeSourceFactory* mimeSourceFactory() const;
+#endif
+ QBrush paper() const;
+ bool linkUnderline() const;
+
+ int heightForWidth(int w) const;
+
+ bool hasSelectedText() const;
+ QString selectedText() const;
+ bool isUndoAvailable() const;
+ bool isRedoAvailable() const;
+
+ WordWrap wordWrap() const;
+ int wrapColumnOrWidth() const;
+ WrapPolicy wrapPolicy() const;
+
+ int tabStopWidth() const;
+
+ QString anchorAt(const QPoint& pos, Qt::AnchorAttribute a = Qt::AnchorHref);
+
+ QSize sizeHint() const;
+
+ bool isReadOnly() const { return readonly; }
+
+ void getCursorPosition(int *parag, int *index) const;
+
+ bool isModified() const;
+ bool italic() const;
+ bool bold() const;
+ bool underline() const;
+ QString family() const;
+ int pointSize() const;
+ QColor color() const;
+ QFont font() const;
+ QFont currentFont() const;
+ int alignment() const;
+ VerticalAlignment verticalAlignment() const;
+ int undoDepth() const;
+
+ // do not use, will go away
+ virtual bool getFormat(int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment);
+ // do not use, will go away
+ virtual bool getParagraphFormat(int para, QFont *font, QColor *color,
+ VerticalAlignment *verticalAlignment, int *alignment,
+ Q3StyleSheetItem::DisplayMode *displayMode,
+ Q3StyleSheetItem::ListStyle *listStyle,
+ int *listDepth);
+
+
+ bool isOverwriteMode() const { return overWrite; }
+ QColor paragraphBackgroundColor(int para) const;
+
+ bool isUndoRedoEnabled() const;
+ bool eventFilter(QObject *o, QEvent *e);
+ bool tabChangesFocus() const;
+
+ void setAutoFormatting(AutoFormatting);
+ AutoFormatting autoFormatting() const;
+ Q3SyntaxHighlighter *syntaxHighlighter() const;
+
+public Q_SLOTS:
+#ifndef QT_NO_MIME
+ virtual void setMimeSourceFactory(Q3MimeSourceFactory* factory);
+#endif
+ virtual void setStyleSheet(Q3StyleSheet* styleSheet);
+ virtual void scrollToAnchor(const QString& name);
+ virtual void setPaper(const QBrush& pap);
+ virtual void setLinkUnderline(bool);
+
+ virtual void setWordWrap(Q3TextEdit::WordWrap mode);
+ virtual void setWrapColumnOrWidth(int);
+ virtual void setWrapPolicy(Q3TextEdit::WrapPolicy policy);
+
+ virtual void copy();
+ virtual void append(const QString& text);
+
+ void setText(const QString &txt) { setText(txt, QString()); }
+ virtual void setText(const QString &txt, const QString &context);
+ virtual void setTextFormat(Qt::TextFormat f);
+
+ virtual void selectAll(bool select = true);
+ virtual void setTabStopWidth(int ts);
+ virtual void zoomIn(int range);
+ virtual void zoomIn() { zoomIn(1); }
+ virtual void zoomOut(int range);
+ virtual void zoomOut() { zoomOut(1); }
+ virtual void zoomTo(int size);
+
+ virtual void sync();
+ virtual void setReadOnly(bool b);
+
+ virtual void undo();
+ virtual void redo();
+ virtual void cut();
+ virtual void paste();
+#ifndef QT_NO_CLIPBOARD
+ virtual void pasteSubType(const QByteArray &subtype);
+#endif
+ virtual void clear();
+ virtual void del();
+ virtual void indent();
+ virtual void setItalic(bool b);
+ virtual void setBold(bool b);
+ virtual void setUnderline(bool b);
+ virtual void setFamily(const QString &f);
+ virtual void setPointSize(int s);
+ virtual void setColor(const QColor &c);
+ virtual void setVerticalAlignment(Q3TextEdit::VerticalAlignment a);
+ virtual void setAlignment(int a);
+
+ // do not use, will go away
+ virtual void setParagType(Q3StyleSheetItem::DisplayMode dm, Q3StyleSheetItem::ListStyle listStyle);
+
+ virtual void setCursorPosition(int parag, int index);
+ virtual void setSelection(int parag_from, int index_from, int parag_to, int index_to, int selNum = 0);
+ virtual void setSelectionAttributes(int selNum, const QColor &back, bool invertText);
+ virtual void setModified(bool m);
+ virtual void resetFormat();
+ virtual void setUndoDepth(int d);
+ virtual void setFormat(Q3TextFormat *f, int flags);
+ virtual void ensureCursorVisible();
+ virtual void placeCursor(const QPoint &pos, Q3TextCursor *c = 0);
+ virtual void moveCursor(Q3TextEdit::CursorAction action, bool select);
+ virtual void doKeyboardAction(Q3TextEdit::KeyboardAction action);
+ virtual void removeSelectedText(int selNum = 0);
+ virtual void removeSelection(int selNum = 0);
+ virtual void setCurrentFont(const QFont &f);
+ virtual void setOverwriteMode(bool b) { overWrite = b; }
+
+ virtual void scrollToBottom();
+
+ virtual void insert(const QString &text, uint insertionFlags = CheckNewLines | RemoveSelected);
+
+ // obsolete
+ virtual void insert(const QString &text, bool, bool = true, bool = true);
+
+ virtual void insertAt(const QString &text, int para, int index);
+ virtual void removeParagraph(int para);
+ virtual void insertParagraph(const QString &text, int para);
+
+ virtual void setParagraphBackgroundColor(int para, const QColor &bg);
+ virtual void clearParagraphBackground(int para);
+
+ virtual void setUndoRedoEnabled(bool b);
+ virtual void setTabChangesFocus(bool b);
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ void polishEvent(QEvent*);
+ void setMaxLogLines(int numLines);
+ int maxLogLines() const;
+#endif
+
+Q_SIGNALS:
+ void textChanged();
+ void selectionChanged();
+ void copyAvailable(bool);
+ void undoAvailable(bool yes);
+ void redoAvailable(bool yes);
+ void currentFontChanged(const QFont &f);
+ void currentColorChanged(const QColor &c);
+ void currentAlignmentChanged(int a);
+ void currentVerticalAlignmentChanged(Q3TextEdit::VerticalAlignment a);
+ void cursorPositionChanged(Q3TextCursor *c);
+ void cursorPositionChanged(int para, int pos);
+ void returnPressed();
+ void modificationChanged(bool m);
+ void clicked(int parag, int index);
+ void doubleClicked(int parag, int index);
+
+protected:
+ void repaintChanged();
+ void updateStyles();
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ bool event(QEvent *e);
+ void changeEvent(QEvent *);
+ void keyPressEvent(QKeyEvent *e);
+ void resizeEvent(QResizeEvent *e);
+ void viewportResizeEvent(QResizeEvent*);
+ void contentsMousePressEvent(QMouseEvent *e);
+ void contentsMouseMoveEvent(QMouseEvent *e);
+ void contentsMouseReleaseEvent(QMouseEvent *e);
+ void contentsMouseDoubleClickEvent(QMouseEvent *e);
+#ifndef QT_NO_WHEELEVENT
+ void contentsWheelEvent(QWheelEvent *e);
+#endif
+ void inputMethodEvent(QInputMethodEvent *);
+#ifndef QT_NO_DRAGANDDROP
+ void contentsDragEnterEvent(QDragEnterEvent *e);
+ void contentsDragMoveEvent(QDragMoveEvent *e);
+ void contentsDragLeaveEvent(QDragLeaveEvent *e);
+ void contentsDropEvent(QDropEvent *e);
+#endif
+ void contentsContextMenuEvent(QContextMenuEvent *e);
+ bool focusNextPrevChild(bool next);
+ Q3TextDocument *document() const;
+ Q3TextCursor *textCursor() const;
+ void setDocument(Q3TextDocument *doc);
+ virtual Q3PopupMenu *createPopupMenu(const QPoint& pos);
+ virtual Q3PopupMenu *createPopupMenu();
+ void drawCursor(bool visible);
+
+protected Q_SLOTS:
+ virtual void doChangeInterval();
+ virtual void sliderReleased();
+
+private Q_SLOTS:
+ void formatMore();
+ void doResize();
+ void autoScrollTimerDone();
+ void blinkCursor();
+ void setModified();
+ void startDrag();
+ void documentWidthChanged(int w);
+ void clipboardChanged();
+
+private:
+ struct Q_COMPAT_EXPORT UndoRedoInfo {
+ enum Type { Invalid, Insert, Delete, Backspace, Return, RemoveSelected, Format, Style, IME };
+
+ UndoRedoInfo(Q3TextDocument *dc);
+ ~UndoRedoInfo();
+ void clear();
+ bool valid() const;
+
+ QUndoRedoInfoPrivate *d;
+ int id;
+ int index;
+ int eid;
+ int eindex;
+ Q3TextFormat *format;
+ int flags;
+ Type type;
+ Q3TextDocument *doc;
+ QByteArray styleInformation;
+ };
+
+private:
+ void updateCursor(const QPoint & pos);
+ void handleMouseMove(const QPoint& pos);
+ void drawContents(QPainter *);
+ virtual bool linksEnabled() const { return false; }
+ void init();
+ void checkUndoRedoInfo(UndoRedoInfo::Type t);
+ void updateCurrentFormat();
+ bool handleReadOnlyKeyEvent(QKeyEvent *e);
+ void makeParagVisible(Q3TextParagraph *p);
+ void normalCopy();
+ void copyToClipboard();
+#ifndef QT_NO_MIME
+ QByteArray pickSpecial(QMimeSource* ms, bool always_ask, const QPoint&);
+ Q3TextDrag *dragObject(QWidget *parent = 0) const;
+#endif
+#ifndef QT_NO_MIMECLIPBOARD
+ void pasteSpecial(const QPoint&);
+#endif
+ void setFontInternal(const QFont &f);
+
+ virtual void emitHighlighted(const QString &) {}
+ virtual void emitLinkClicked(const QString &) {}
+
+ void readFormats(Q3TextCursor &c1, Q3TextCursor &c2, Q3TextString &text, bool fillStyles = false);
+ void clearUndoRedo();
+ void paintDocument(bool drawAll, QPainter *p, int cx = -1, int cy = -1, int cw = -1, int ch = -1);
+ void moveCursor(CursorAction action);
+ void ensureFormatted(Q3TextParagraph *p);
+ void placeCursor(const QPoint &pos, Q3TextCursor *c, bool link);
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ bool checkOptimMode();
+ QString optimText() const;
+ void optimSetText(const QString &str);
+ void optimAppend(const QString &str);
+ void optimInsert(const QString &str, int line, int index);
+ void optimDrawContents(QPainter * p, int cx, int cy, int cw, int ch);
+ void optimMousePressEvent(QMouseEvent * e);
+ void optimMouseReleaseEvent(QMouseEvent * e);
+ void optimMouseMoveEvent(QMouseEvent * e);
+ int optimCharIndex(const QString &str, int mx) const;
+ void optimSelectAll();
+ void optimRemoveSelection();
+ void optimSetSelection(int startLine, int startIdx, int endLine,
+ int endIdx);
+ bool optimHasSelection() const;
+ QString optimSelectedText() const;
+ bool optimFind(const QString & str, bool, bool, bool, int *, int *);
+ void optimParseTags(QString * str, int lineNo = -1, int indexOffset = 0);
+ Q3TextEditOptimPrivate::Tag * optimPreviousLeftTag(int line);
+ void optimSetTextFormat(Q3TextDocument *, Q3TextCursor *, Q3TextFormat * f,
+ int, int, Q3TextEditOptimPrivate::Tag * t);
+ Q3TextEditOptimPrivate::Tag * optimAppendTag(int index, const QString & tag);
+ Q3TextEditOptimPrivate::Tag * optimInsertTag(int line, int index, const QString & tag);
+ void optimCheckLimit(const QString& str);
+ bool optimHasBoldMetrics(int line);
+
+private Q_SLOTS:
+ void optimDoAutoScroll();
+#endif // QT_TEXTEDIT_OPTIMIZATION
+
+private:
+#ifndef QT_NO_CLIPBOARD
+ void pasteSubType(const QByteArray &subtype, QMimeSource *m);
+#endif
+
+private:
+ Q_DISABLE_COPY(Q3TextEdit)
+
+ Q3TextDocument *doc;
+ Q3TextCursor *cursor;
+ QTimer *formatTimer, *scrollTimer, *changeIntervalTimer, *blinkTimer, *dragStartTimer;
+ Q3TextParagraph *lastFormatted;
+ int interval;
+ UndoRedoInfo undoRedoInfo;
+ Q3TextFormat *currentFormat;
+ int currentAlignment;
+ QPoint oldMousePos, mousePos;
+ QPoint dragStartPos;
+ QString onLink;
+ WordWrap wrapMode;
+ WrapPolicy wPolicy;
+ int wrapWidth;
+ QString pressedLink;
+ Q3TextEditPrivate *d;
+ bool inDoubleClick : 1;
+ bool mousePressed : 1;
+ bool cursorVisible : 1;
+ bool blinkCursorVisible : 1;
+ bool modified : 1;
+ bool mightStartDrag : 1;
+ bool inDnD : 1;
+ bool readonly : 1;
+ bool undoEnabled : 1;
+ bool overWrite : 1;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Q3TextEdit::AutoFormatting)
+
+inline Q3TextDocument *Q3TextEdit::document() const
+{
+ return doc;
+}
+
+inline Q3TextCursor *Q3TextEdit::textCursor() const
+{
+ return cursor;
+}
+
+inline void Q3TextEdit::setCurrentFont(const QFont &f)
+{
+ Q3TextEdit::setFontInternal(f);
+}
+
+#endif // QT_NO_TEXTEDIT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTEDIT_H
diff --git a/src/qt3support/text/q3textstream.cpp b/src/qt3support/text/q3textstream.cpp
new file mode 100644
index 0000000..94e4ec3
--- /dev/null
+++ b/src/qt3support/text/q3textstream.cpp
@@ -0,0 +1,2436 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textstream.h"
+#include <qdebug.h>
+
+#ifndef QT_NO_TEXTSTREAM
+#include "qtextcodec.h"
+#include "qregexp.h"
+#include "qbuffer.h"
+#include "qfile.h"
+#include "q3cstring.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef Q_OS_WINCE
+#include <locale.h>
+#endif
+
+#if defined(Q_OS_WIN32)
+#include "qt_windows.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TEXTCODEC
+static void resetCodecConverterState(QTextCodec::ConverterState *state) {
+ state->flags = QTextCodec::DefaultConversion;
+ state->remainingChars = state->invalidChars =
+ state->state_data[0] = state->state_data[1] = state->state_data[2] = 0;
+ if (state->d) qFree(state->d);
+ state->d = 0;
+}
+#endif
+
+/*!
+ \class Q3TextStream
+ \compat
+ \reentrant
+ \brief The Q3TextStream class provides basic functions for reading
+ and writing text using a QIODevice.
+
+ The text stream class has a functional interface that is very
+ similar to that of the standard C++ iostream class.
+
+ Qt provides several global functions similar to the ones in iostream:
+ \table
+ \header \i Function \i Meaning
+ \row \i bin \i sets the Q3TextStream to read/write binary numbers
+ \row \i oct \i sets the Q3TextStream to read/write octal numbers
+ \row \i dec \i sets the Q3TextStream to read/write decimal numbers
+ \row \i hex \i sets the Q3TextStream to read/write hexadecimal numbers
+ \row \i endl \i forces a line break
+ \row \i flush \i forces the QIODevice to flush any buffered data
+ \row \i ws \i eats any available whitespace (on input)
+ \row \i reset \i resets the Q3TextStream to its default mode (see reset())
+ \row \i qSetW(int) \i sets the \link width() field width \endlink
+ to the given argument
+ \row \i qSetFill(int) \i sets the \link fill() fill character
+ \endlink to the given argument
+ \row \i qSetPrecision(int) \i sets the \link precision() precision
+ \endlink to the given argument
+ \endtable
+
+ \warning By default Q3TextStream will automatically detect whether
+ integers in the stream are in decimal, octal, hexadecimal or
+ binary format when reading from the stream. In particular, a
+ leading '0' signifies octal, i.e. the sequence "0100" will be
+ interpreted as 64.
+
+ The Q3TextStream class reads and writes text; it is not appropriate
+ for dealing with binary data (but QDataStream is).
+
+ By default, output of Unicode text (i.e. QString) is done using
+ the local 8-bit encoding. This can be changed using the
+ setEncoding() method. For input, the Q3TextStream will auto-detect
+ standard Unicode "byte order marked" text files; otherwise the
+ local 8-bit encoding is used.
+
+ The QIODevice is set in the constructor, or later using
+ setDevice(). If the end of the input is reached atEnd() returns
+ TRUE. Data can be read into variables of the appropriate type
+ using the operator>>() overloads, or read in its entirety into a
+ single string using read(), or read a line at a time using
+ readLine(). Whitespace can be skipped over using skipWhiteSpace().
+ You can set flags for the stream using flags() or setf(). The
+ stream also supports width(), precision() and fill(); use reset()
+ to reset the defaults.
+
+ \sa QDataStream
+*/
+
+/*!
+ \enum Q3TextStream::Encoding
+
+ \value Locale
+ \value Latin1
+ \value Unicode
+ \value UnicodeNetworkOrder
+ \value UnicodeReverse
+ \value RawUnicode
+ \value UnicodeUTF8
+
+ See setEncoding() for an explanation of the encodings.
+*/
+
+/*
+ \class QTSManip
+ \internal
+*/
+
+#if defined(QT_CHECK_STATE)
+#undef CHECK_STREAM_PRECOND
+#define CHECK_STREAM_PRECOND if ( !dev ) { \
+ qWarning( "Q3TextStream: No device" ); \
+ return *this; }
+#else
+#define CHECK_STREAM_PRECOND
+#endif
+
+
+#define I_SHORT 0x0010
+#define I_INT 0x0020
+#define I_LONG 0x0030
+#define I_TYPE_MASK 0x00f0
+
+#define I_BASE_2 Q3TextStream::bin
+#define I_BASE_8 Q3TextStream::oct
+#define I_BASE_10 Q3TextStream::dec
+#define I_BASE_16 Q3TextStream::hex
+#define I_BASE_MASK (Q3TextStream::bin | Q3TextStream::oct | Q3TextStream::dec | Q3TextStream::hex)
+
+#define I_SIGNED 0x0100
+#define I_UNSIGNED 0x0200
+#define I_SIGN_MASK 0x0f00
+
+
+static const QChar QEOF = QChar((ushort)0xffff); //guaranteed not to be a character.
+static const uint getline_buf_size = 256; // bufsize used by ts_getline()
+
+const int Q3TextStream::basefield = I_BASE_MASK;
+const int Q3TextStream::adjustfield = ( Q3TextStream::left |
+ Q3TextStream::right |
+ Q3TextStream::internal );
+const int Q3TextStream::floatfield = ( Q3TextStream::scientific |
+ Q3TextStream::fixed );
+
+
+class Q3TextStreamPrivate {
+public:
+#ifndef QT_NO_TEXTCODEC
+ Q3TextStreamPrivate()
+ : sourceType( NotSet ) { }
+ ~Q3TextStreamPrivate() {
+ }
+#else
+ Q3TextStreamPrivate() : sourceType( NotSet ) { }
+ ~Q3TextStreamPrivate() { }
+#endif
+ QString ungetcBuf;
+
+ enum SourceType { NotSet, IODevice, String, ByteArray, File };
+ SourceType sourceType;
+};
+
+
+// skips whitespace and returns the first non-whitespace character
+QChar Q3TextStream::eat_ws()
+{
+ QChar c;
+ do { c = ts_getc(); } while ( c != QEOF && ts_isspace(c) );
+ return c;
+}
+
+void Q3TextStream::init()
+{
+ // ### ungetcBuf = QEOF;
+ dev = 0;
+ owndev = FALSE;
+ mapper = 0;
+#ifndef QT_NO_TEXTCODEC
+ resetCodecConverterState(&mapperReadState);
+ resetCodecConverterState(&mapperWriteState);
+#endif
+ d = new Q3TextStreamPrivate;
+ doUnicodeHeader = TRUE; // autodetect
+ latin1 = TRUE; // should use locale?
+ internalOrder = QChar::networkOrdered();
+ networkOrder = TRUE;
+}
+
+/*!
+ Constructs a data stream that has no IO device.
+*/
+
+Q3TextStream::Q3TextStream()
+{
+ init();
+ setEncoding( Locale );
+ reset();
+ d->sourceType = Q3TextStreamPrivate::NotSet;
+}
+
+/*!
+ Constructs a text stream that uses the IO device \a iod.
+*/
+
+Q3TextStream::Q3TextStream( QIODevice *iod )
+{
+ init();
+ setEncoding( Locale );
+ dev = iod;
+ reset();
+ d->sourceType = Q3TextStreamPrivate::IODevice;
+}
+
+// TODO: use special-case handling of this case in Q3TextStream, and
+// simplify this class to only deal with QChar or QString data.
+class QStringBuffer : public QIODevice {
+public:
+ QStringBuffer( QString* str );
+ ~QStringBuffer();
+ bool open( OpenMode m );
+ void close();
+ qint64 size() const;
+
+protected:
+ qint64 readData( char *p, qint64 len );
+ qint64 writeData( const char *p, qint64 len );
+
+ QString* s;
+
+private:
+ QStringBuffer( const QStringBuffer & );
+ QStringBuffer &operator=( const QStringBuffer & );
+};
+
+
+QStringBuffer::QStringBuffer( QString* str )
+{
+ s = str;
+}
+
+QStringBuffer::~QStringBuffer()
+{
+}
+
+
+bool QStringBuffer::open( OpenMode m )
+{
+ if ( !s ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "QStringBuffer::open: No string" );
+#endif
+ return FALSE;
+ }
+ if ( isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "QStringBuffer::open: Buffer already open" );
+#endif
+ return FALSE;
+ }
+ setOpenMode( m );
+ if ( m & QIODevice::Truncate )
+ s->truncate( 0 );
+
+ if ( m & QIODevice::Append ) {
+ seek(s->length()*sizeof(QChar));
+ } else {
+ seek(0);
+ }
+ return TRUE;
+}
+
+void QStringBuffer::close()
+{
+ if ( isOpen() ) {
+ seek(0);
+ QIODevice::close();
+ }
+}
+
+qint64 QStringBuffer::size() const
+{
+ return s ? s->length()*sizeof(QChar) : 0;
+}
+
+qint64 QStringBuffer::readData( char *p, qint64 len )
+{
+#if defined(QT_CHECK_STATE)
+ Q_CHECK_PTR( p );
+ if ( !isOpen() ) {
+ qWarning( "QStringBuffer::readBlock: Buffer not open" );
+ return qint64(-1);
+ }
+ if ( !isReadable() ) {
+ qWarning( "QStringBuffer::readBlock: Read operation not permitted" );
+ return qint64(-1);
+ }
+#endif
+ if ( pos() + len > qint64(s->length()*sizeof(QChar)) ) {
+ // overflow
+ if ( pos() >= qint64(s->length()*sizeof(QChar)) ) {
+ return -1;
+ } else {
+ len = s->length()*2 - pos();
+ }
+ }
+ memcpy( p, ((const char*)(s->unicode()))+pos(), len );
+ return len;
+}
+
+qint64 QStringBuffer::writeData( const char *p, qint64 len )
+{
+#if defined(QT_CHECK_NULL)
+ if ( p == 0 && len != 0 )
+ qWarning( "QStringBuffer::writeBlock: Null pointer error" );
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "QStringBuffer::writeBlock: Buffer not open" );
+ return -1;
+ }
+ if ( !isWritable() ) {
+ qWarning( "QStringBuffer::writeBlock: Write operation not permitted" );
+ return -1;
+ }
+ if ( pos()&1 ) {
+ qWarning( "QStringBuffer::writeBlock: non-even index - non Unicode" );
+ return -1;
+ }
+ if ( len&1 ) {
+ qWarning( "QStringBuffer::writeBlock: non-even length - non Unicode" );
+ return -1;
+ }
+#endif
+ s->replace(pos()/2, len/2, (QChar*)p, len/2);
+ return len;
+}
+
+/*!
+ Constructs a text stream that operates on the Unicode QString, \a
+ str, through an internal device. The \a filemode argument is
+ passed to the device's open() function; see \l{QIODevice::mode()}.
+
+ If you set an encoding or codec with setEncoding() or setCodec(),
+ this setting is ignored for text streams that operate on QString.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 0
+
+ Writing data to the text stream will modify the contents of the
+ string. The string will be expanded when data is written beyond
+ the end of the string. Note that the string will not be truncated:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 1
+
+ Note that because QString is Unicode, you should not use
+ readRawBytes() or writeRawBytes() on such a stream.
+*/
+
+Q3TextStream::Q3TextStream( QString* str, int filemode )
+{
+ // TODO: optimize for this case as it becomes more common
+ // (see QStringBuffer above)
+ init();
+ dev = new QStringBuffer( str );
+ ((QStringBuffer *)dev)->open( QIODevice::OpenMode(filemode) );
+ owndev = TRUE;
+ setEncoding(RawUnicode);
+ reset();
+ d->sourceType = Q3TextStreamPrivate::String;
+}
+
+/*! \obsolete
+
+ This constructor is equivalent to the constructor taking a QString*
+ parameter.
+*/
+
+Q3TextStream::Q3TextStream( QString& str, int filemode )
+{
+ init();
+ dev = new QStringBuffer( &str );
+ ((QStringBuffer *)dev)->open( QIODevice::OpenMode(filemode) );
+ owndev = TRUE;
+ setEncoding(RawUnicode);
+ reset();
+ d->sourceType = Q3TextStreamPrivate::String;
+}
+
+/*!
+ Constructs a text stream that operates on the byte array, \a a,
+ through an internal QBuffer device. The \a mode argument is passed
+ to the device's open() function; see \l{QIODevice::mode()}.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 2
+
+ Writing data to the text stream will modify the contents of the
+ array. The array will be expanded when data is written beyond the
+ end of the string.
+
+ Same example, using a QBuffer:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 3
+*/
+
+Q3TextStream::Q3TextStream( QByteArray &a, int mode )
+{
+ init();
+ QBuffer *buffer = new QBuffer;
+ buffer->setBuffer( &a );
+ buffer->open( QIODevice::OpenMode(mode) );
+ dev = buffer;
+ owndev = TRUE;
+ setEncoding( Latin1 ); //### Locale???
+ reset();
+ d->sourceType = Q3TextStreamPrivate::ByteArray;
+}
+
+/*!
+ Constructs a text stream that operates on an existing file handle
+ \a fh through an internal QFile device. The \a mode argument is
+ passed to the device's open() function; see \l{QIODevice::mode()}.
+
+ Note that if you create a Q3TextStream \c cout or another name that
+ is also used for another variable of a different type, some
+ linkers may confuse the two variables, which will often cause
+ crashes.
+*/
+
+Q3TextStream::Q3TextStream( FILE *fh, int mode )
+{
+ init();
+ setEncoding( Locale ); //###
+ dev = new QFile;
+ ((QFile *)dev)->open( QIODevice::OpenMode(mode), fh );
+ owndev = TRUE;
+ reset();
+ d->sourceType = Q3TextStreamPrivate::File;
+}
+
+/*!
+ Destroys the text stream.
+
+ The destructor does not affect the current IO device.
+*/
+
+Q3TextStream::~Q3TextStream()
+{
+ if ( owndev )
+ delete dev;
+ delete d;
+}
+
+/*!
+ \since 4.2
+
+ Positions the read pointer at the first non-whitespace character.
+*/
+void Q3TextStream::skipWhiteSpace()
+{
+ ts_ungetc( eat_ws() );
+}
+
+
+/*!
+ Tries to read \a len characters from the stream and stores them in
+ \a buf. Returns the number of characters really read.
+
+ \warning There will no QEOF appended if the read reaches the end
+ of the file. EOF is reached when the return value does not equal
+ \a len.
+*/
+uint Q3TextStream::ts_getbuf( QChar* buf, uint len )
+{
+ if ( len < 1 )
+ return 0;
+
+ uint rnum = 0; // the number of QChars really read
+
+ if ( d && d->ungetcBuf.length() ) {
+ while ( rnum < len && rnum < uint(d->ungetcBuf.length()) ) {
+ *buf = d->ungetcBuf.constref( rnum );
+ buf++;
+ rnum++;
+ }
+ d->ungetcBuf = d->ungetcBuf.mid( rnum );
+ if ( rnum >= len )
+ return rnum;
+ }
+
+ // we use dev->ungetch() for one of the bytes of the unicode
+ // byte-order mark, but a local unget hack for the other byte:
+ int ungetHack = EOF;
+
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE; // only at the top
+ int c1 = dev->getch();
+ if ( c1 == EOF )
+ return rnum;
+ int c2 = dev->getch();
+ if ( c1 == 0xfe && c2 == 0xff ) {
+ mapper = 0;
+ latin1 = FALSE;
+ internalOrder = QChar::networkOrdered();
+ networkOrder = TRUE;
+ } else if ( c1 == 0xff && c2 == 0xfe ) {
+ mapper = 0;
+ latin1 = FALSE;
+ internalOrder = !QChar::networkOrdered();
+ networkOrder = FALSE;
+ } else {
+ if ( c2 != EOF ) {
+ dev->ungetch( c2 );
+ ungetHack = c1;
+ } else {
+ /*
+ A small bug might hide here. If only the first byte
+ of a file has made it so far, and that first byte
+ is half of the byte-order mark, then the utfness
+ will not be detected.
+ */
+ dev->ungetch( c1 );
+ }
+ }
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ bool shortRead = FALSE;
+ while( rnum < len ) {
+ QString s;
+ bool readBlock = !( len == 1+rnum );
+ for (;;) {
+ // for efficiency: normally read a whole block
+ if ( readBlock ) {
+ // guess buffersize; this may be wrong (too small or too
+ // big). But we can handle this (either iterate reading
+ // or use ungetcBuf).
+ // Note that this might cause problems for codecs where
+ // one byte can result in >1 Unicode Characters if bytes
+ // are written to the stream in the meantime (loss of
+ // synchronicity).
+ uint rlen = len - rnum;
+ char *cbuf = new char[ rlen ];
+ if ( ungetHack != EOF ) {
+ rlen = 1+dev->readBlock( cbuf+1, rlen-1 );
+ cbuf[0] = (char)ungetHack;
+ ungetHack = EOF;
+ } else {
+ rlen = dev->readBlock( cbuf, rlen );
+ }
+ s += mapper->toUnicode( cbuf, rlen, &mapperWriteState );
+ delete[] cbuf;
+ // use buffered reading only for the first time, because we
+ // have to get the stream synchronous again (this is easier
+ // with single character reading)
+ readBlock = FALSE;
+ }
+ // get stream (and codec) in sync
+ int c;
+ if ( ungetHack == EOF ) {
+ c = dev->getch();
+ } else {
+ c = ungetHack;
+ ungetHack = EOF;
+ }
+ if ( c == EOF ) {
+ shortRead = TRUE;
+ break;
+ }
+ char b = c;
+ uint lengthBefore = s.length();
+ s += mapper->toUnicode( &b, 1, &mapperWriteState );
+
+ if ( uint(s.length()) > lengthBefore )
+ break; // it seems we are in sync now
+ }
+ uint i = 0;
+ uint end = QMIN( len-rnum, uint(s.length()) );
+ while( i < end ) {
+ *buf = s.constref(i++);
+ buf++;
+ }
+ rnum += end;
+ if ( uint(s.length()) > i ) {
+ // could be = but append is clearer
+ d->ungetcBuf.append( s.mid( i ) );
+ }
+ if ( shortRead )
+ return rnum;
+ }
+ } else
+#endif
+ if ( latin1 ) {
+ if ( len == 1+rnum ) {
+ // use this method for one character because it is more efficient
+ // (arnt doubts whether it makes a difference, but lets it stand)
+ int c = (ungetHack == EOF) ? dev->getch() : ungetHack;
+ if ( c != EOF ) {
+ *buf = QLatin1Char((char)c);
+ buf++;
+ rnum++;
+ }
+ } else {
+ if ( ungetHack != EOF ) {
+ *buf = QLatin1Char((char)ungetHack);
+ buf++;
+ rnum++;
+ ungetHack = EOF;
+ }
+ char *cbuf = new char[len - rnum];
+ while ( !dev->atEnd() && rnum < len ) {
+ uint rlen = len - rnum;
+ rlen = dev->readBlock( cbuf, rlen );
+ char *it = cbuf;
+ char *end = cbuf + rlen;
+ while ( it < end ) {
+ *buf = QLatin1Char(*it);
+ buf++;
+ it++;
+ }
+ rnum += rlen;
+ }
+ delete[] cbuf;
+ }
+ } else { // UCS-2 or UTF-16
+ if ( len == 1+rnum ) {
+ int c1 = (ungetHack == EOF) ? dev->getch() : ungetHack;
+
+
+ if ( c1 == EOF )
+ return rnum;
+ int c2 = dev->getch();
+
+
+ if ( c2 == EOF )
+ return rnum;
+
+ if ( networkOrder ) {
+ *buf = QChar( c2, c1 );
+ } else {
+ *buf = QChar( c1, c2 );
+ }
+ buf++;
+ rnum++;
+ } else {
+ char *cbuf = new char[ 2*( len - rnum ) ]; // for paranoids: overflow possible
+ while ( !dev->atEnd() && rnum < len ) {
+ uint rlen = 2 * ( len-rnum );
+ if ( ungetHack != EOF ) {
+ rlen = 1+dev->readBlock( cbuf+1, rlen-1 );
+ cbuf[0] = (char)ungetHack;
+ ungetHack = EOF;
+ } else {
+ rlen = dev->readBlock( cbuf, rlen );
+ }
+ // We can't use an odd number of bytes, so put it back. But
+ // do it only if we are capable of reading more -- normally
+ // there should not be an odd number, but the file might be
+ // truncated or not in UTF-16...
+ if ( (rlen & 1) == 1 )
+ if ( !dev->atEnd() )
+ dev->ungetch( cbuf[--rlen] );
+ uint i = 0;
+ if ( networkOrder ) {
+ while( i < rlen ) {
+ *buf = QChar( cbuf[i+1], cbuf[i] );
+ buf++;
+ i+=2;
+ }
+ } else {
+ while( i < rlen ) {
+ *buf = QChar( cbuf[i], cbuf[i+1] );
+ buf++;
+ i+=2;
+ }
+ }
+ rnum += i/2;
+ }
+ delete[] cbuf;
+ }
+ }
+ return rnum;
+}
+
+/*!
+ Tries to read one line, but at most len characters from the stream
+ and stores them in \a buf.
+
+ Returns the number of characters really read. Newlines are not
+ stripped.
+
+ There will be a QEOF appended if the read reaches the end of file;
+ this is different to ts_getbuf().
+
+ This function works only if a newline (as byte) is also a newline
+ (as resulting character) since it uses QIODevice::readLine(). So
+ use it only for such codecs where this is true!
+
+ This function is (almost) a no-op for UTF 16. Don't use it if
+ doUnicodeHeader is TRUE!
+*/
+uint Q3TextStream::ts_getline( QChar* buf )
+{
+ uint rnum=0; // the number of QChars really read
+ char cbuf[ getline_buf_size+1 ];
+
+ if ( d && d->ungetcBuf.length() ) {
+ while( rnum < getline_buf_size && rnum < uint(d->ungetcBuf.length()) ) {
+ buf[rnum] = d->ungetcBuf.constref(rnum);
+ rnum++;
+ }
+ d->ungetcBuf = d->ungetcBuf.mid( rnum );
+ if ( rnum >= getline_buf_size )
+ return rnum;
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ QString s;
+ bool readBlock = TRUE;
+ for (;;) {
+ // for efficiency: try to read a line
+ if ( readBlock ) {
+ int rlen = getline_buf_size - rnum;
+ rlen = dev->readLine( cbuf, rlen+1 );
+ if ( rlen == -1 )
+ rlen = 0;
+ s += mapper->toUnicode( cbuf, rlen, &mapperWriteState );
+ readBlock = FALSE;
+ }
+ if ( dev->atEnd()
+ || s.at( s.length()-1 ) == QLatin1Char('\n')
+ || s.at( s.length()-1 ) == QLatin1Char('\r')
+ ) {
+ break;
+ } else {
+ // get stream (and codec) in sync
+ int c;
+ c = dev->getch();
+ if ( c == EOF ) {
+ break;
+ }
+ char b = c;
+ uint lengthBefore = s.length();
+ s += mapper->toUnicode( &b, 1, &mapperWriteState );
+ if ( uint(s.length()) > lengthBefore )
+ break; // it seems we are in sync now
+ }
+ }
+ uint i = 0;
+ while( rnum < getline_buf_size && i < uint(s.length()) )
+ buf[rnum++] = s.constref(i++);
+ if ( uint(s.length()) > i )
+ // could be = but append is clearer
+ d->ungetcBuf.append( s.mid( i ) );
+ if ( rnum < getline_buf_size && dev->atEnd() )
+ buf[rnum++] = QEOF;
+ } else
+#endif
+ if ( latin1 ) {
+ int rlen = getline_buf_size - rnum;
+ rlen = dev->readLine( cbuf, rlen+1 );
+ if ( rlen == -1 )
+ rlen = 0;
+ char *end = cbuf+rlen;
+ char *it = cbuf;
+ buf +=rnum;
+ while ( it != end ) {
+ buf->setCell( *(it++) );
+ buf->setRow( 0 );
+ buf++;
+ }
+ rnum += rlen;
+ if ( rnum < getline_buf_size && dev->atEnd() )
+ buf[1] = QEOF;
+ }
+ return rnum;
+}
+
+
+/*!
+ Puts one character into the stream.
+*/
+void Q3TextStream::ts_putc( QChar c )
+{
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ int len = 1;
+ QString s = c;
+ Q3CString block = mapper->fromUnicode( s.data(), len );//, &mapperReadState );
+ dev->writeBlock( block );
+ } else
+#endif
+ if ( latin1 ) {
+ if ( c.row() )
+ dev->putch( '?' ); // unknown character
+ else
+ dev->putch( c.cell() );
+ } else {
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE;
+ ts_putc( QChar::ByteOrderMark );
+ }
+ if ( internalOrder ) {
+ // this case is needed by QStringBuffer
+ dev->writeBlock( (char*)&c, sizeof(QChar) );
+ } else if ( networkOrder ) {
+ dev->putch( c.row() );
+ dev->putch( c.cell() );
+ } else {
+ dev->putch( c.cell() );
+ dev->putch( c.row() );
+ }
+ }
+}
+
+/*!
+ Puts one character into the stream.
+*/
+void Q3TextStream::ts_putc( int ch )
+{
+ ts_putc( QChar((ushort)ch) );
+}
+
+bool Q3TextStream::ts_isdigit( QChar c )
+{
+ return c.isDigit();
+}
+
+bool Q3TextStream::ts_isspace( QChar c )
+{
+ return c.isSpace();
+}
+
+void Q3TextStream::ts_ungetc( QChar c )
+{
+ if ( c.unicode() == 0xffff )
+ return;
+
+ d->ungetcBuf.prepend( c );
+}
+
+/*!
+ \since 4.2
+
+ Reads \a len bytes from the stream into \a s and returns a
+ reference to the stream.
+
+ The buffer \a s must be preallocated.
+
+ Note that no encoding is done by this function.
+
+ \warning The behavior of this function is undefined unless the
+ stream's encoding is set to Unicode or Latin1.
+
+ \sa QIODevice::readBlock()
+*/
+
+Q3TextStream &Q3TextStream::readRawBytes( char *s, uint len )
+{
+ dev->readBlock( s, len );
+ return *this;
+}
+
+/*!
+ \since 4.2
+
+ Writes the \a len bytes from \a s to the stream and returns a
+ reference to the stream.
+
+ Note that no encoding is done by this function.
+
+ \sa QIODevice::writeBlock()
+*/
+
+Q3TextStream &Q3TextStream::writeRawBytes( const char* s, uint len )
+{
+ dev->writeBlock( s, len );
+ return *this;
+}
+
+
+Q3TextStream &Q3TextStream::writeBlock( const char* p, uint len )
+{
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE;
+ if ( !mapper && !latin1 ) {
+ ts_putc( QChar::ByteOrderMark );
+ }
+ }
+ // QCString and const char * are treated as Latin-1
+ if ( !mapper && latin1 ) {
+ dev->writeBlock( p, len );
+ } else if ( !mapper && internalOrder ) {
+ QChar *u = new QChar[len];
+ for ( uint i = 0; i < len; i++ )
+ u[i] = QLatin1Char(p[i]);
+ dev->writeBlock( (char*)u, len * sizeof(QChar) );
+ delete [] u;
+ }
+#ifndef QT_NO_TEXTCODEC
+ else if (mapper) {
+ QString s = QString::fromLatin1(p, len);
+ int l = len;
+ Q3CString block = mapper->fromUnicode(s.data(), l );//, &mapperReadState );
+ dev->writeBlock( block );
+ }
+#endif
+ else {
+ for ( uint i = 0; i < len; i++ )
+ ts_putc( (uchar)p[i] );
+ }
+ return *this;
+}
+
+Q3TextStream &Q3TextStream::writeBlock( const QChar* p, uint len )
+{
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ QConstString s( p, len );
+ int l = len;
+ Q3CString block = mapper->fromUnicode( s.string().data(), l );//, &mapperReadState );
+ dev->writeBlock( block );
+ } else
+#endif
+ if ( latin1 ) {
+ dev->write(QString( p, len ).toLatin1());
+ } else if ( internalOrder ) {
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE;
+ ts_putc( QChar::ByteOrderMark );
+ }
+ dev->writeBlock( (char*)p, sizeof(QChar)*len );
+ } else {
+ for (uint i=0; i<len; i++)
+ ts_putc( p[i] );
+ }
+ return *this;
+}
+
+/*!
+ \since 4.2
+
+ Resets the text stream.
+
+ \list
+ \i All flags are set to 0.
+ \i The field width is set to 0.
+ \i The fill character is set to ' ' (Space).
+ \i The precision is set to 6.
+ \endlist
+
+ \sa setf(), width(), fill(), precision()
+*/
+
+void Q3TextStream::reset()
+{
+ fflags = 0;
+ fwidth = 0;
+ fillchar = ' ';
+ fprec = 6;
+}
+
+/*!
+ \fn QIODevice *Q3TextStream::device() const
+ \since 4.2
+
+ Returns the IO device currently set.
+
+ \sa setDevice(), unsetDevice()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the IO device to \a iod.
+
+ \sa device(), unsetDevice()
+*/
+
+void Q3TextStream::setDevice( QIODevice *iod )
+{
+ if ( owndev ) {
+ delete dev;
+ owndev = FALSE;
+ }
+ dev = iod;
+ d->sourceType = Q3TextStreamPrivate::IODevice;
+}
+
+/*!
+ \since 4.2
+
+ Unsets the IO device. Equivalent to setDevice( 0 ).
+
+ \sa device(), setDevice()
+*/
+
+void Q3TextStream::unsetDevice()
+{
+ setDevice( 0 );
+ d->sourceType = Q3TextStreamPrivate::NotSet;
+}
+
+/*!
+ \fn bool Q3TextStream::atEnd() const
+ \since 4.2
+
+ Returns TRUE if the IO device has reached the end position (end of
+ the stream or file) or if there is no IO device set; otherwise
+ returns FALSE.
+
+ \sa QIODevice::atEnd()
+*/
+
+/*!\fn bool Q3TextStream::eof() const
+
+ \obsolete
+
+ This function has been renamed to atEnd().
+
+ \sa QIODevice::atEnd()
+*/
+
+/*****************************************************************************
+ Q3TextStream read functions
+ *****************************************************************************/
+
+
+/*!
+ \overload
+
+ Reads a char \a c from the stream and returns a reference to the
+ stream. Note that whitespace is skipped.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( char &c )
+{
+ CHECK_STREAM_PRECOND
+ c = eat_ws().toLatin1();
+ return *this;
+}
+
+/*!
+ Reads a char \a c from the stream and returns a reference to the
+ stream. Note that whitespace is \e not skipped.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( QChar &c )
+{
+ CHECK_STREAM_PRECOND
+ c = ts_getc();
+ return *this;
+}
+
+
+ulong Q3TextStream::input_bin()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ int dv = ch.digitValue();
+ while ( dv == 0 || dv == 1 ) {
+ val = ( val << 1 ) + dv;
+ ch = ts_getc();
+ dv = ch.digitValue();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+ulong Q3TextStream::input_oct()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ int dv = ch.digitValue();
+ while ( dv >= 0 && dv <= 7 ) {
+ val = ( val << 3 ) + dv;
+ ch = ts_getc();
+ dv = ch.digitValue();
+ }
+ if ( dv == 8 || dv == 9 ) {
+ while ( ts_isdigit(ch) )
+ ch = ts_getc();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+ulong Q3TextStream::input_dec()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ int dv = ch.digitValue();
+ while ( ts_isdigit(ch) ) {
+ val = val * 10 + dv;
+ ch = ts_getc();
+ dv = ch.digitValue();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+ulong Q3TextStream::input_hex()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ char c = ch.toLatin1();
+ while ( isxdigit((uchar) c) ) {
+ val <<= 4;
+ if ( ts_isdigit(QLatin1Char(c)) )
+ val += c - '0';
+ else
+ val += 10 + tolower( (uchar) c ) - 'a';
+ ch = ts_getc();
+ c = ch.toLatin1();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+long Q3TextStream::input_int()
+{
+ long val;
+ QChar ch;
+ char c;
+ switch ( flags() & basefield ) {
+ case bin:
+ val = (long)input_bin();
+ break;
+ case oct:
+ val = (long)input_oct();
+ break;
+ case dec:
+ ch = eat_ws();
+ c = ch.toLatin1();
+ if ( ch == QEOF ) {
+ val = 0;
+ } else {
+ if ( !(c == '-' || c == '+') )
+ ts_ungetc( ch );
+ if ( c == '-' ) {
+ ulong v = input_dec();
+ if ( v ) { // ensure that LONG_MIN can be read
+ v--;
+ val = -((long)v) - 1;
+ } else {
+ val = 0;
+ }
+ } else {
+ val = (long)input_dec();
+ }
+ }
+ break;
+ case hex:
+ val = (long)input_hex();
+ break;
+ default:
+ val = 0;
+ ch = eat_ws();
+ c = ch.toLatin1();
+ if ( c == '0' ) { // bin, oct or hex
+ ch = ts_getc();
+ c = ch.toLatin1();
+ if ( tolower((uchar) c) == 'x' )
+ val = (long)input_hex();
+ else if ( tolower((uchar) c) == 'b' )
+ val = (long)input_bin();
+ else { // octal
+ ts_ungetc( ch );
+ if ( c >= '0' && c <= '7' ) {
+ val = (long)input_oct();
+ } else {
+ val = 0;
+ }
+ }
+ } else if ( ts_isdigit(ch) ) {
+ ts_ungetc( ch );
+ val = (long)input_dec();
+ } else if ( c == '-' || c == '+' ) {
+ ulong v = input_dec();
+ if ( c == '-' ) {
+ if ( v ) { // ensure that LONG_MIN can be read
+ v--;
+ val = -((long)v) - 1;
+ } else {
+ val = 0;
+ }
+ } else {
+ val = (long)v;
+ }
+ }
+ }
+ return val;
+}
+
+//
+// We use a table-driven FSM to parse floating point numbers
+// strtod() cannot be used directly since we're reading from a QIODevice
+//
+
+double Q3TextStream::input_double()
+{
+ const int Init = 0; // states
+ const int Sign = 1;
+ const int Mantissa = 2;
+ const int Dot = 3;
+ const int Abscissa = 4;
+ const int ExpMark = 5;
+ const int ExpSign = 6;
+ const int Exponent = 7;
+ const int Done = 8;
+
+ const int InputSign = 1; // input tokens
+ const int InputDigit = 2;
+ const int InputDot = 3;
+ const int InputExp = 4;
+
+ static const uchar table[8][5] = {
+ /* None InputSign InputDigit InputDot InputExp */
+ { 0, Sign, Mantissa, Dot, 0, }, // Init
+ { 0, 0, Mantissa, Dot, 0, }, // Sign
+ { Done, Done, Mantissa, Dot, ExpMark,}, // Mantissa
+ { 0, 0, Abscissa, 0, 0, }, // Dot
+ { Done, Done, Abscissa, Done, ExpMark,}, // Abscissa
+ { 0, ExpSign, Exponent, 0, 0, }, // ExpMark
+ { 0, 0, Exponent, 0, 0, }, // ExpSign
+ { Done, Done, Exponent, Done, Done } // Exponent
+ };
+
+ int state = Init; // parse state
+ int input; // input token
+
+ char buf[256];
+ int i = 0;
+ QChar c = eat_ws();
+
+ for (;;) {
+
+ switch ( c.toLatin1() ) {
+ case '+':
+ case '-':
+ input = InputSign;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ input = InputDigit;
+ break;
+ case '.':
+ input = InputDot;
+ break;
+ case 'e':
+ case 'E':
+ input = InputExp;
+ break;
+ default:
+ input = 0;
+ break;
+ }
+
+ state = table[state][input];
+
+ if ( state == 0 || state == Done || i > 250 ) {
+ if ( i > 250 ) { // ignore rest of digits
+ do { c = ts_getc(); } while ( c != QEOF && ts_isdigit(c) );
+ }
+ if ( c != QEOF )
+ ts_ungetc( c );
+ buf[i] = '\0';
+ char *end;
+ return strtod( buf, &end );
+ }
+
+ buf[i++] = c.toLatin1();
+ c = ts_getc();
+ }
+
+#if !defined(Q_CC_EDG)
+ return 0.0;
+#endif
+}
+
+
+/*!
+ \overload
+
+ Reads a signed \c short integer \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( signed short &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (signed short)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads an unsigned \c short integer \a i from the stream and
+ returns a reference to the stream. See flags() for an explanation
+ of the expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( unsigned short &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (unsigned short)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a signed \c int \a i from the stream and returns a reference
+ to the stream. See flags() for an explanation of the expected
+ input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( signed int &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (signed int)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads an unsigned \c int \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( unsigned int &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (unsigned int)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a signed \c long int \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( signed long &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (signed long)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads an unsigned \c long int \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( unsigned long &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (unsigned long)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a \c float \a f from the stream and returns a reference to
+ the stream. See flags() for an explanation of the expected input
+ format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( float &f )
+{
+ CHECK_STREAM_PRECOND
+ f = (float)input_double();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a \c double \a f from the stream and returns a reference to
+ the stream. See flags() for an explanation of the expected input
+ format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( double &f )
+{
+ CHECK_STREAM_PRECOND
+ f = input_double();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a "word" from the stream into \a s and returns a reference
+ to the stream.
+
+ A word consists of characters for which isspace() returns FALSE.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( char *s )
+{
+ CHECK_STREAM_PRECOND
+ int maxlen = width( 0 );
+ QChar c = eat_ws();
+ if ( !maxlen )
+ maxlen = -1;
+ while ( c != QEOF ) {
+ if ( ts_isspace(c) || maxlen-- == 0 ) {
+ ts_ungetc( c );
+ break;
+ }
+ *s++ = c.toLatin1();
+ c = ts_getc();
+ }
+
+ *s = '\0';
+ return *this;
+}
+
+/*!
+ \overload
+
+ Reads a "word" from the stream into \a str and returns a reference
+ to the stream.
+
+ A word consists of characters for which isspace() returns FALSE.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( QString &str )
+{
+ CHECK_STREAM_PRECOND
+ str=QString::fromLatin1("");
+ QChar c = eat_ws();
+
+ while ( c != QEOF ) {
+ if ( ts_isspace(c) ) {
+ ts_ungetc( c );
+ break;
+ }
+
+ str += c;
+ c = ts_getc();
+ }
+
+ return *this;
+}
+
+/*!
+ \overload
+
+ Reads a "word" from the stream into \a str and returns a reference
+ to the stream.
+
+ A word consists of characters for which isspace() returns FALSE.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( Q3CString &str )
+{
+ CHECK_STREAM_PRECOND
+ Q3CString *dynbuf = 0;
+ const int buflen = 256;
+ char buffer[buflen];
+ char *s = buffer;
+ int i = 0;
+ QChar c = eat_ws();
+
+ while ( c != QEOF ) {
+ if ( ts_isspace(c) ) {
+ ts_ungetc( c );
+ break;
+ }
+ if ( i >= buflen-1 ) {
+ if ( !dynbuf ) { // create dynamic buffer
+ dynbuf = new Q3CString(buflen*2);
+ memcpy( dynbuf->data(), s, i ); // copy old data
+ } else if ( i >= (int)dynbuf->size()-1 ) {
+ dynbuf->resize( dynbuf->size()*2 );
+ }
+ s = dynbuf->data();
+ }
+ s[i++] = c.toLatin1();
+ c = ts_getc();
+ }
+ str.resize( i );
+ memcpy( str.data(), s, i );
+
+ delete dynbuf;
+ return *this;
+}
+
+
+/*!
+ \since 4.2
+
+ Reads a line from the stream and returns a string containing the
+ text.
+
+ The returned string does not contain any trailing newline or
+ carriage return. Note that this is different from
+ QIODevice::readLine(), which does not strip the newline at the end
+ of the line.
+
+ On EOF you will get a QString that is null. On reading an empty
+ line the returned QString is empty but not null.
+
+ \sa QIODevice::readLine()
+*/
+
+QString Q3TextStream::readLine()
+{
+#if defined(QT_CHECK_STATE)
+ if ( !dev ) {
+ qWarning( "Q3TextStream::readLine: No device" );
+ return QString::null;
+ }
+#endif
+ bool readCharByChar = TRUE;
+ QString result;
+#if 0
+ if ( !doUnicodeHeader && (
+ (latin1) ||
+ (mapper != 0 && mapper->mibEnum() == 106 ) // UTF 8
+ ) ) {
+ readCharByChar = FALSE;
+ // use optimized read line
+ QChar c[getline_buf_size];
+ int pos = 0;
+ bool eof = FALSE;
+
+ for (;;) {
+ pos = ts_getline( c );
+ if ( pos == 0 ) {
+ // something went wrong; try fallback
+ readCharByChar = TRUE;
+ //dev->resetStatus();
+ break;
+ }
+ if ( c[pos-1] == QEOF || c[pos-1] == '\n' ) {
+ if ( pos>2 && c[pos-1]==QEOF && c[pos-2]=='\n' ) {
+ result += QString( c, pos-2 );
+ } else if ( pos > 1 ) {
+ result += QString( c, pos-1 );
+ }
+ if ( pos == 1 && c[pos-1] == QEOF )
+ eof = TRUE;
+ break;
+ } else {
+ result += QString( c, pos );
+ }
+ }
+ if ( eof && result.isEmpty() )
+ return QString::null;
+ }
+#endif
+ if ( readCharByChar ) {
+ const int buf_size = 256;
+ QChar c[buf_size];
+ int pos = 0;
+
+ c[pos] = ts_getc();
+ if ( c[pos] == QEOF )
+ return QString::null;
+
+ while ( c[pos] != QEOF && c[pos] != QLatin1Char('\n') ) {
+ if ( c[pos] == QLatin1Char('\r') ) { // ( handle mac and dos )
+ QChar nextc = ts_getc();
+ if ( nextc != QLatin1Char('\n') )
+ ts_ungetc( nextc );
+ break;
+ }
+ pos++;
+ if ( pos >= buf_size ) {
+ result += QString( c, pos );
+ pos = 0;
+ }
+ c[pos] = ts_getc();
+ }
+ result += QString( c, pos );
+ }
+
+ return result;
+}
+
+
+/*!
+ \since 4.2
+
+ Reads the entire stream from the current position, and returns a string
+ containing the text.
+
+ \sa readLine()
+*/
+
+QString Q3TextStream::read()
+{
+#if defined(QT_CHECK_STATE)
+ if ( !dev ) {
+ qWarning( "Q3TextStream::read: No device" );
+ return QString::null;
+ }
+#endif
+ QString result;
+ const uint bufsize = 512;
+ QChar buf[bufsize];
+ uint i, num, start;
+ bool skipped_cr = FALSE;
+
+ for (;;) {
+ num = ts_getbuf(buf,bufsize);
+ // convert dos (\r\n) and mac (\r) style eol to unix style (\n)
+ start = 0;
+ for ( i=0; i<num; i++ ) {
+ if ( buf[i] == QLatin1Char('\r') ) {
+ // Only skip single cr's preceding lf's
+ if ( skipped_cr ) {
+ result += buf[i];
+ start++;
+ } else {
+ result += QString( &buf[start], i-start );
+ start = i+1;
+ skipped_cr = TRUE;
+ }
+ } else {
+ if ( skipped_cr ) {
+ if ( buf[i] != QLatin1Char('\n') ) {
+ // Should not have skipped it
+ result += QLatin1Char('\n');
+ }
+ skipped_cr = FALSE;
+ }
+ }
+ }
+ if ( start < num )
+ result += QString( &buf[start], i-start );
+ if ( num != bufsize ) // if ( EOF )
+ break;
+ }
+ return result;
+}
+
+
+
+/*****************************************************************************
+ Q3TextStream write functions
+ *****************************************************************************/
+
+/*!
+ \since 4.2
+
+ Writes character \c char to the stream and returns a reference to
+ the stream.
+
+ The character \a c is assumed to be Latin1 encoded independent of
+ the Encoding set for the Q3TextStream.
+*/
+Q3TextStream &Q3TextStream::operator<<( QChar c )
+{
+ CHECK_STREAM_PRECOND
+ ts_putc( c );
+ return *this;
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes character \a c to the stream and returns a reference to the
+ stream.
+*/
+Q3TextStream &Q3TextStream::operator<<( char c )
+{
+ CHECK_STREAM_PRECOND
+ unsigned char uc = (unsigned char) c;
+ ts_putc( uc );
+ return *this;
+}
+
+Q3TextStream &Q3TextStream::output_int( int format, ulong n, bool neg )
+{
+ static const char hexdigits_lower[] = "0123456789abcdef";
+ static const char hexdigits_upper[] = "0123456789ABCDEF";
+ CHECK_STREAM_PRECOND
+ char buf[76];
+ register char *p;
+ int len;
+ const char *hexdigits;
+
+ switch ( flags() & I_BASE_MASK ) {
+
+ case I_BASE_2: // output binary number
+ switch ( format & I_TYPE_MASK ) {
+ case I_SHORT: len=16; break;
+ case I_INT: len=sizeof(int)*8; break;
+ case I_LONG: len=32; break;
+ default: len = 0;
+ }
+ p = &buf[74]; // go reverse order
+ *p = '\0';
+ while ( len-- ) {
+ *--p = (char)(n&1) + '0';
+ n >>= 1;
+ if ( !n )
+ break;
+ }
+ if ( flags() & showbase ) { // show base
+ *--p = (flags() & uppercase) ? 'B' : 'b';
+ *--p = '0';
+ }
+ break;
+
+ case I_BASE_8: // output octal number
+ p = &buf[74];
+ *p = '\0';
+ do {
+ *--p = (char)(n&7) + '0';
+ n >>= 3;
+ } while ( n );
+ if ( flags() & showbase )
+ *--p = '0';
+ break;
+
+ case I_BASE_16: // output hexadecimal number
+ p = &buf[74];
+ *p = '\0';
+ hexdigits = (flags() & uppercase) ?
+ hexdigits_upper : hexdigits_lower;
+ do {
+ *--p = hexdigits[(int)n&0xf];
+ n >>= 4;
+ } while ( n );
+ if ( flags() & showbase ) {
+ *--p = (flags() & uppercase) ? 'X' : 'x';
+ *--p = '0';
+ }
+ break;
+
+ default: // decimal base is default
+ p = &buf[74];
+ *p = '\0';
+ if ( neg )
+ n = (ulong)(-(long)n);
+ do {
+ *--p = ((int)(n%10)) + '0';
+ n /= 10;
+ } while ( n );
+ if ( neg )
+ *--p = '-';
+ else if ( flags() & showpos )
+ *--p = '+';
+ if ( (flags() & internal) && fwidth && !ts_isdigit(QLatin1Char(*p)) ) {
+ ts_putc( *p ); // special case for internal
+ ++p; // padding
+ fwidth--;
+ return *this << (const char*)p;
+ }
+ }
+ if ( fwidth ) { // adjustment required
+ if ( !(flags() & left) ) { // but NOT left adjustment
+ len = qstrlen(p);
+ int padlen = fwidth - len;
+ if ( padlen <= 0 ) { // no padding required
+ writeBlock( p, len );
+ } else if ( padlen < (int)(p-buf) ) { // speeds up padding
+ memset( p-padlen, (char)fillchar, padlen );
+ writeBlock( p-padlen, padlen+len );
+ }
+ else // standard padding
+ *this << (const char*)p;
+ }
+ else
+ *this << (const char*)p;
+ fwidth = 0; // reset field width
+ }
+ else {
+ writeBlock( p, qstrlen(p) );
+ }
+ return *this;
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c short integer \a i to the stream and returns a
+ reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( signed short i )
+{
+ return output_int( I_SHORT | I_SIGNED, i, i < 0 );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c unsigned \c short integer \a i to the stream and
+ returns a reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( unsigned short i )
+{
+ return output_int( I_SHORT | I_UNSIGNED, i, FALSE );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c int \a i to the stream and returns a reference to the
+ stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( signed int i )
+{
+ return output_int( I_INT | I_SIGNED, i, i < 0 );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c unsigned \c int \a i to the stream and returns a
+ reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( unsigned int i )
+{
+ return output_int( I_INT | I_UNSIGNED, i, FALSE );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c long \c int \a i to the stream and returns a reference
+ to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( signed long i )
+{
+ return output_int( I_LONG | I_SIGNED, i, i < 0 );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c unsigned \c long \c int \a i to the stream and
+ returns a reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( unsigned long i )
+{
+ return output_int( I_LONG | I_UNSIGNED, i, FALSE );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c float \a f to the stream and returns a reference to
+ the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( float f )
+{
+ return *this << (double)f;
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c double \a f to the stream and returns a reference to
+ the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( double f )
+{
+ CHECK_STREAM_PRECOND
+ char f_char;
+ char format[16];
+ if ( (flags()&floatfield) == fixed )
+ f_char = 'f';
+ else if ( (flags()&floatfield) == scientific )
+ f_char = (flags() & uppercase) ? 'E' : 'e';
+ else
+ f_char = (flags() & uppercase) ? 'G' : 'g';
+ register char *fs = format; // generate format string
+ *fs++ = '%'; // "%.<prec>l<f_char>"
+ *fs++ = '.';
+ int prec = precision();
+ if ( prec > 99 )
+ prec = 99;
+ if ( prec >= 10 ) {
+ *fs++ = prec / 10 + '0';
+ *fs++ = prec % 10 + '0';
+ } else {
+ *fs++ = prec + '0';
+ }
+ *fs++ = 'l';
+ *fs++ = f_char;
+ *fs = '\0';
+ QString num;
+ num.sprintf(format, f); // convert to text
+ if ( fwidth ) // padding
+ *this << num.latin1();
+ else // just write it
+ writeBlock(num.latin1(), num.length());
+ return *this;
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a string to the stream and returns a reference to the
+ stream.
+
+ The string \a s is assumed to be Latin1 encoded independent of the
+ Encoding set for the Q3TextStream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( const char* s )
+{
+ CHECK_STREAM_PRECOND
+ char padbuf[48];
+ uint len = qstrlen( s ); // don't write null terminator
+ if ( fwidth ) { // field width set
+ int padlen = fwidth - len;
+ fwidth = 0; // reset width
+ if ( padlen > 0 ) {
+ char *ppad;
+ if ( padlen > 46 ) { // create extra big fill buffer
+ ppad = new char[padlen];
+ Q_CHECK_PTR( ppad );
+ } else {
+ ppad = padbuf;
+ }
+ memset( ppad, (char)fillchar, padlen ); // fill with fillchar
+ if ( !(flags() & left) ) {
+ writeBlock( ppad, padlen );
+ padlen = 0;
+ }
+ writeBlock( s, len );
+ if ( padlen )
+ writeBlock( ppad, padlen );
+ if ( ppad != padbuf ) // delete extra big fill buf
+ delete[] ppad;
+ return *this;
+ }
+ }
+ writeBlock( s, len );
+ return *this;
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes \a s to the stream and returns a reference to the stream.
+
+ The string \a s is assumed to be Latin1 encoded independent of the
+ Encoding set for the Q3TextStream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( const Q3CString & s )
+{
+ return operator<<(s.data());
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes \a s to the stream and returns a reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( const QString& s )
+{
+ if ( !mapper && latin1 )
+ return operator<<(s.latin1());
+ CHECK_STREAM_PRECOND
+ QString s1 = s;
+ if ( fwidth ) { // field width set
+ if ( !(flags() & left) ) {
+ s1 = s.rightJustify(fwidth, QLatin1Char((char)fillchar));
+ } else {
+ s1 = s.leftJustify(fwidth, QLatin1Char((char)fillchar));
+ }
+ fwidth = 0; // reset width
+ }
+ writeBlock( s1.unicode(), s1.length() );
+ return *this;
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a pointer to the stream and returns a reference to the
+ stream.
+
+ The \a ptr is output as an unsigned long hexadecimal integer.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( void *ptr )
+{
+ int f = flags();
+ setf( hex, basefield );
+ setf( showbase );
+ unsetf( uppercase );
+ output_int( I_LONG | I_UNSIGNED, (quintptr)ptr, FALSE );
+ flags( f );
+ return *this;
+}
+
+
+/*!
+ \fn int Q3TextStream::flags() const
+ \since 4.2
+
+ Returns the current stream flags. The default value is 0.
+
+ \table
+ \header \i Flag \i Meaning
+ \row \i \c skipws \i Not currently used; whitespace always skipped
+ \row \i \c left \i Numeric fields are left-aligned
+ \row \i \c right
+ \i Not currently used (by default, numerics are right-aligned)
+ \row \i \c internal \i Puts any padding spaces between +/- and value
+ \row \i \c bin \i Output \e and input only in binary
+ \row \i \c oct \i Output \e and input only in octal
+ \row \i \c dec \i Output \e and input only in decimal
+ \row \i \c hex \i Output \e and input only in hexadecimal
+ \row \i \c showbase
+ \i Annotates numeric outputs with 0b, 0, or 0x if in \c bin,
+ \c oct, or \c hex format
+ \row \i \c showpoint \i Not currently used
+ \row \i \c uppercase \i Uses 0B and 0X rather than 0b and 0x
+ \row \i \c showpos \i Shows + for positive numeric values
+ \row \i \c scientific \i Uses scientific notation for floating point values
+ \row \i \c fixed \i Uses fixed-point notation for floating point values
+ \endtable
+
+ Note that unless \c bin, \c oct, \c dec, or \c hex is set, the
+ input base is octal if the value starts with 0, hexadecimal if it
+ starts with 0x, binary if it starts with 0b, and decimal
+ otherwise.
+
+ \sa setf(), unsetf()
+*/
+
+/*!
+ \fn int Q3TextStream::flags( int f )
+
+ \overload
+
+ Sets the stream flags to \a f. Returns the previous stream flags.
+
+ \sa setf(), unsetf(), flags()
+*/
+
+/*!
+ \fn int Q3TextStream::setf( int bits )
+ \since 4.2
+
+ Sets the stream flag bits \a bits. Returns the previous stream
+ flags.
+
+ Equivalent to \c{flags( flags() | bits )}.
+
+ \sa unsetf()
+*/
+
+/*!
+ \fn int Q3TextStream::setf( int bits, int mask )
+
+ \overload
+
+ Sets the stream flag bits \a bits with a bit mask \a mask. Returns
+ the previous stream flags.
+
+ Equivalent to \c{flags( (flags() & ~mask) | (bits & mask) )}.
+
+ \sa setf(), unsetf()
+*/
+
+/*!
+ \fn int Q3TextStream::unsetf( int bits )
+ \since 4.2
+
+ Clears the stream flag bits \a bits. Returns the previous stream
+ flags.
+
+ Equivalent to \c{flags( flags() & ~mask )}.
+
+ \sa setf()
+*/
+
+/*!
+ \fn int Q3TextStream::width() const
+ \since 4.2
+
+ Returns the field width. The default value is 0.
+*/
+
+/*!
+ \fn int Q3TextStream::width( int w )
+
+ \overload
+
+ Sets the field width to \a w. Returns the previous field width.
+*/
+
+/*!
+ \fn int Q3TextStream::fill() const
+ \since 4.2
+
+ Returns the fill character. The default value is ' ' (space).
+*/
+
+/*!
+ \fn int Q3TextStream::fill( int f )
+ \overload
+
+ Sets the fill character to \a f. Returns the previous fill character.
+*/
+
+/*!
+ \fn int Q3TextStream::precision() const
+ \since 4.2
+
+ Returns the precision. The default value is 6.
+*/
+
+/*!
+ \fn int Q3TextStream::precision( int p )
+
+ \overload
+
+ Sets the precision to \a p. Returns the previous precision setting.
+*/
+
+
+Q3TextStream &bin( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::bin,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &oct( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::oct,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &dec( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::dec,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &hex( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::hex,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &endl( Q3TextStream &s )
+{
+ return s << '\n';
+}
+
+Q3TextStream &flush( Q3TextStream &s )
+{
+ return s;
+}
+
+Q3TextStream &ws( Q3TextStream &s )
+{
+ s.skipWhiteSpace();
+ return s;
+}
+
+Q3TextStream &reset( Q3TextStream &s )
+{
+ s.reset();
+ return s;
+}
+
+/*!
+ \since 4.2
+
+ Sets the encoding of this stream to \a e, where \a e is one of the
+ following values:
+ \table
+ \header \i Encoding \i Meaning
+ \row \i Locale
+ \i Uses local file format (Latin1 if locale is not set), but
+ autodetecting Unicode(utf16) on input.
+ \row \i Unicode
+ \i Uses Unicode(utf16) for input and output. Output will be
+ written in the order most efficient for the current platform
+ (i.e. the order used internally in QString).
+ \row \i UnicodeUTF8
+ \i Using Unicode(utf8) for input and output. If you use it for
+ input it will autodetect utf16 and use it instead of utf8.
+ \row \i Latin1
+ \i ISO-8859-1. Will not autodetect utf16.
+ \row \i UnicodeNetworkOrder
+ \i Uses network order Unicode(utf16) for input and output.
+ Useful when reading Unicode data that does not start with the
+ byte order marker.
+ \row \i UnicodeReverse
+ \i Uses reverse network order Unicode(utf16) for input and
+ output. Useful when reading Unicode data that does not start
+ with the byte order marker or when writing data that should be
+ read by buggy Windows applications.
+ \row \i RawUnicode
+ \i Like Unicode, but does not write the byte order marker nor
+ does it auto-detect the byte order. Useful only when writing to
+ non-persistent storage used by a single process.
+ \endtable
+
+ \c Locale and all Unicode encodings, except \c RawUnicode, will look
+ at the first two bytes in an input stream to determine the byte
+ order. The initial byte order marker will be stripped off before
+ data is read.
+
+ Note that this function should be called before any data is read to
+ or written from the stream.
+
+ \sa setCodec()
+*/
+
+void Q3TextStream::setEncoding( Encoding e )
+{
+ resetCodecConverterState(&mapperReadState);
+ resetCodecConverterState(&mapperWriteState);
+
+ if ( d->sourceType == Q3TextStreamPrivate::String )
+ return;
+
+ switch ( e ) {
+ case Unicode:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = TRUE;
+ networkOrder = QChar::networkOrdered();
+ break;
+ case UnicodeUTF8:
+#ifndef QT_NO_TEXTCODEC
+ mapper = QTextCodec::codecForMib( 106 );
+ mapperWriteState.flags |= QTextCodec::IgnoreHeader;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = TRUE;
+ networkOrder = QChar::networkOrdered();
+#else
+ mapper = 0;
+ latin1 = TRUE;
+ doUnicodeHeader = TRUE;
+#endif
+ break;
+ case UnicodeNetworkOrder:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = QChar::networkOrdered();
+ networkOrder = TRUE;
+ break;
+ case UnicodeReverse:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = !QChar::networkOrdered();
+ networkOrder = FALSE;
+ break;
+ case RawUnicode:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = FALSE;
+ internalOrder = TRUE;
+ networkOrder = QChar::networkOrdered();
+ break;
+ case Locale:
+ latin1 = TRUE; // fallback to Latin-1
+#ifndef QT_NO_TEXTCODEC
+ mapper = QTextCodec::codecForLocale();
+ mapperReadState.flags |= QTextCodec::IgnoreHeader;
+ mapperWriteState.flags |= QTextCodec::IgnoreHeader;
+ // optimized Latin-1 processing
+#if defined(Q_OS_WIN32)
+ if ( GetACP() == 1252 )
+ mapper = 0;
+#endif
+ if ( mapper && mapper->mibEnum() == 4 )
+#endif
+ mapper = 0;
+
+ doUnicodeHeader = TRUE; // If it reads as Unicode, accept it
+ break;
+ case Latin1:
+ mapper = 0;
+ doUnicodeHeader = FALSE;
+ latin1 = TRUE;
+ break;
+ }
+}
+
+
+#ifndef QT_NO_TEXTCODEC
+/*!
+ \since 4.2
+
+ Sets the codec for this stream to \a codec. Will not try to
+ autodetect Unicode.
+
+ Note that this function should be called before any data is read
+ to/written from the stream.
+
+ \sa setEncoding(), codec()
+*/
+
+void Q3TextStream::setCodec( QTextCodec *codec )
+{
+ if ( d->sourceType == Q3TextStreamPrivate::String )
+ return; // QString does not need any codec
+ mapper = codec;
+ latin1 = ( codec->mibEnum() == 4 );
+ if ( latin1 )
+ mapper = 0;
+ doUnicodeHeader = FALSE;
+}
+
+/*!
+ Returns the codec actually used for this stream.
+ \since 4.2
+
+ If Unicode is automatically detected in input, a codec with \link
+ QTextCodec::name() name() \endlink "ISO-10646-UCS-2" is returned.
+
+ \sa setCodec()
+*/
+
+QTextCodec *Q3TextStream::codec()
+{
+ if ( mapper ) {
+ return mapper;
+ } else {
+ // 4 is "ISO 8859-1", 1000 is "ISO-10646-UCS-2"
+ return QTextCodec::codecForMib( latin1 ? 4 : 1000 );
+ }
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTSTREAM
diff --git a/src/qt3support/text/q3textstream.h b/src/qt3support/text/q3textstream.h
new file mode 100644
index 0000000..e37ff51
--- /dev/null
+++ b/src/qt3support/text/q3textstream.h
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTSTREAM_H
+#define Q3TEXTSTREAM_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qstring.h>
+#ifndef QT_NO_TEXTCODEC
+#include <QtCore/qtextcodec.h>
+#endif
+#include <Qt3Support/q3cstring.h>
+
+#include <stdio.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3TextStreamPrivate;
+
+class Q_COMPAT_EXPORT Q3TextStream // text stream class
+{
+public:
+ enum Encoding { Locale, Latin1, Unicode, UnicodeNetworkOrder,
+ UnicodeReverse, RawUnicode, UnicodeUTF8 };
+
+ void setEncoding( Encoding );
+#ifndef QT_NO_TEXTCODEC
+ void setCodec( QTextCodec* );
+ QTextCodec *codec();
+#endif
+
+ Q3TextStream();
+ Q3TextStream( QIODevice * );
+ Q3TextStream( QString*, int mode );
+ Q3TextStream( QString&, int mode ); // obsolete
+ Q3TextStream( QByteArray&, int mode );
+ Q3TextStream( FILE *, int mode );
+ virtual ~Q3TextStream();
+
+ QIODevice *device() const;
+ void setDevice( QIODevice * );
+ void unsetDevice();
+
+ bool atEnd() const;
+ bool eof() const;
+
+ Q3TextStream &operator>>( QChar & );
+ Q3TextStream &operator>>( char & );
+ Q3TextStream &operator>>( signed short & );
+ Q3TextStream &operator>>( unsigned short & );
+ Q3TextStream &operator>>( signed int & );
+ Q3TextStream &operator>>( unsigned int & );
+ Q3TextStream &operator>>( signed long & );
+ Q3TextStream &operator>>( unsigned long & );
+ Q3TextStream &operator>>( float & );
+ Q3TextStream &operator>>( double & );
+ Q3TextStream &operator>>( char * );
+ Q3TextStream &operator>>( QString & );
+ Q3TextStream &operator>>( Q3CString & );
+
+ Q3TextStream &operator<<( QChar );
+ Q3TextStream &operator<<( char );
+ Q3TextStream &operator<<( signed short );
+ Q3TextStream &operator<<( unsigned short );
+ Q3TextStream &operator<<( signed int );
+ Q3TextStream &operator<<( unsigned int );
+ Q3TextStream &operator<<( signed long );
+ Q3TextStream &operator<<( unsigned long );
+ Q3TextStream &operator<<( float );
+ Q3TextStream &operator<<( double );
+ Q3TextStream &operator<<( const char* );
+ Q3TextStream &operator<<( const QString & );
+ Q3TextStream &operator<<( const Q3CString & );
+ Q3TextStream &operator<<( void * ); // any pointer
+
+ Q3TextStream &readRawBytes( char *, uint len );
+ Q3TextStream &writeRawBytes( const char* , uint len );
+
+ QString readLine();
+ QString read();
+ void skipWhiteSpace();
+
+ enum {
+ skipws = 0x0001, // skip whitespace on input
+ left = 0x0002, // left-adjust output
+ right = 0x0004, // right-adjust output
+ internal = 0x0008, // pad after sign
+ bin = 0x0010, // binary format integer
+ oct = 0x0020, // octal format integer
+ dec = 0x0040, // decimal format integer
+ hex = 0x0080, // hex format integer
+ showbase = 0x0100, // show base indicator
+ showpoint = 0x0200, // force decimal point (float)
+ uppercase = 0x0400, // upper-case hex output
+ showpos = 0x0800, // add '+' to positive integers
+ scientific= 0x1000, // scientific float output
+ fixed = 0x2000 // fixed float output
+ };
+
+ static const int basefield; // bin | oct | dec | hex
+ static const int adjustfield; // left | right | internal
+ static const int floatfield; // scientific | fixed
+
+ int flags() const;
+ int flags( int f );
+ int setf( int bits );
+ int setf( int bits, int mask );
+ int unsetf( int bits );
+
+ void reset();
+
+ int width() const;
+ int width( int );
+ int fill() const;
+ int fill( int );
+ int precision() const;
+ int precision( int );
+
+private:
+ long input_int();
+ void init();
+ Q3TextStream &output_int( int, ulong, bool );
+ QIODevice *dev;
+
+ int fflags;
+ int fwidth;
+ int fillchar;
+ int fprec;
+ bool doUnicodeHeader;
+ bool owndev;
+ QTextCodec *mapper;
+ QTextCodec::ConverterState mapperReadState;
+ QTextCodec::ConverterState mapperWriteState;
+ Q3TextStreamPrivate * d;
+ QChar unused1; // ### remove in Qt 4.0
+ bool latin1;
+ bool internalOrder;
+ bool networkOrder;
+ void *unused2; // ### remove in Qt 4.0
+
+ QChar eat_ws();
+ uint ts_getline( QChar* );
+ void ts_ungetc( QChar );
+ QChar ts_getc();
+ uint ts_getbuf( QChar*, uint );
+ void ts_putc(int);
+ void ts_putc(QChar);
+ bool ts_isspace(QChar);
+ bool ts_isdigit(QChar);
+ ulong input_bin();
+ ulong input_oct();
+ ulong input_dec();
+ ulong input_hex();
+ double input_double();
+ Q3TextStream &writeBlock( const char* p, uint len );
+ Q3TextStream &writeBlock( const QChar* p, uint len );
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3TextStream( const Q3TextStream & );
+ Q3TextStream &operator=( const Q3TextStream & );
+#endif
+};
+
+/*****************************************************************************
+ Q3TextStream inline functions
+ *****************************************************************************/
+
+inline QIODevice *Q3TextStream::device() const
+{ return dev; }
+
+inline bool Q3TextStream::atEnd() const
+{ return dev ? dev->atEnd() : FALSE; }
+
+inline bool Q3TextStream::eof() const
+{ return atEnd(); }
+
+inline int Q3TextStream::flags() const
+{ return fflags; }
+
+inline int Q3TextStream::flags( int f )
+{ int oldf = fflags; fflags = f; return oldf; }
+
+inline int Q3TextStream::setf( int bits )
+{ int oldf = fflags; fflags |= bits; return oldf; }
+
+inline int Q3TextStream::setf( int bits, int mask )
+{ int oldf = fflags; fflags = (fflags & ~mask) | (bits & mask); return oldf; }
+
+inline int Q3TextStream::unsetf( int bits )
+{ int oldf = fflags; fflags &= ~bits; return oldf; }
+
+inline int Q3TextStream::width() const
+{ return fwidth; }
+
+inline int Q3TextStream::width( int w )
+{ int oldw = fwidth; fwidth = w; return oldw; }
+
+inline int Q3TextStream::fill() const
+{ return fillchar; }
+
+inline int Q3TextStream::fill( int f )
+{ int oldc = fillchar; fillchar = f; return oldc; }
+
+inline int Q3TextStream::precision() const
+{ return fprec; }
+
+inline int Q3TextStream::precision( int p )
+{ int oldp = fprec; fprec = p; return oldp; }
+
+/*!
+ Returns one character from the stream, or EOF.
+*/
+inline QChar Q3TextStream::ts_getc()
+{ QChar r; return ( ts_getbuf( &r,1 ) == 1 ? r : QChar((ushort)0xffff) ); }
+
+/*****************************************************************************
+ Q3TextStream manipulators
+ *****************************************************************************/
+
+typedef Q3TextStream & (*Q3TSFUNC)(Q3TextStream &);// manipulator function
+typedef int (Q3TextStream::*Q3TSMFI)(int); // manipulator w/int argument
+
+class Q_COMPAT_EXPORT Q3TSManip { // text stream manipulator
+public:
+ Q3TSManip( Q3TSMFI m, int a ) { mf=m; arg=a; }
+ void exec( Q3TextStream &s ) { (s.*mf)(arg); }
+private:
+ Q3TSMFI mf; // Q3TextStream member function
+ int arg; // member function argument
+};
+
+Q_COMPAT_EXPORT_INLINE Q3TextStream &operator>>( Q3TextStream &s, Q3TSFUNC f )
+{ return (*f)( s ); }
+
+Q_COMPAT_EXPORT_INLINE Q3TextStream &operator<<( Q3TextStream &s, Q3TSFUNC f )
+{ return (*f)( s ); }
+
+Q_COMPAT_EXPORT_INLINE Q3TextStream &operator<<( Q3TextStream &s, Q3TSManip m )
+{ m.exec(s); return s; }
+
+Q_COMPAT_EXPORT Q3TextStream &bin( Q3TextStream &s ); // set bin notation
+Q_COMPAT_EXPORT Q3TextStream &oct( Q3TextStream &s ); // set oct notation
+Q_COMPAT_EXPORT Q3TextStream &dec( Q3TextStream &s ); // set dec notation
+Q_COMPAT_EXPORT Q3TextStream &hex( Q3TextStream &s ); // set hex notation
+Q_COMPAT_EXPORT Q3TextStream &endl( Q3TextStream &s ); // insert EOL ('\n')
+Q_COMPAT_EXPORT Q3TextStream &flush( Q3TextStream &s ); // flush output
+Q_COMPAT_EXPORT Q3TextStream &ws( Q3TextStream &s ); // eat whitespace on input
+Q_COMPAT_EXPORT Q3TextStream &reset( Q3TextStream &s ); // set default flags
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTSTREAM_H
diff --git a/src/qt3support/text/q3textview.cpp b/src/qt3support/text/q3textview.cpp
new file mode 100644
index 0000000..a01490b
--- /dev/null
+++ b/src/qt3support/text/q3textview.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textview.h"
+
+#ifndef QT_NO_TEXTVIEW
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3TextView
+ \brief The Q3TextView class provides a rich text viewer.
+
+ \compat
+
+ This class wraps a read-only \l Q3TextEdit.
+ Use a \l Q3TextEdit instead, and call setReadOnly(true)
+ to disable editing.
+*/
+
+/*! \internal */
+
+Q3TextView::Q3TextView(const QString& text, const QString& context,
+ QWidget *parent, const char *name)
+ : Q3TextEdit(text, context, parent, name)
+{
+ setReadOnly(true);
+}
+
+/*! \internal */
+
+Q3TextView::Q3TextView(QWidget *parent, const char *name)
+ : Q3TextEdit(parent, name)
+{
+ setReadOnly(true);
+}
+
+/*! \internal */
+
+Q3TextView::~Q3TextView()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/text/q3textview.h b/src/qt3support/text/q3textview.h
new file mode 100644
index 0000000..652dc10
--- /dev/null
+++ b/src/qt3support/text/q3textview.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTVIEW_H
+#define Q3TEXTVIEW_H
+
+#include <Qt3Support/q3textedit.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_TEXTVIEW
+
+class Q_COMPAT_EXPORT Q3TextView : public Q3TextEdit
+{
+ Q_OBJECT
+
+public:
+ Q3TextView(const QString& text, const QString& context = QString(),
+ QWidget* parent=0, const char* name=0);
+ Q3TextView(QWidget* parent=0, const char* name=0);
+
+ virtual ~Q3TextView();
+
+private:
+ Q_DISABLE_COPY(Q3TextView)
+};
+
+#endif // QT_NO_TEXTVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTVIEW_H
diff --git a/src/qt3support/text/text.pri b/src/qt3support/text/text.pri
new file mode 100644
index 0000000..0e74761
--- /dev/null
+++ b/src/qt3support/text/text.pri
@@ -0,0 +1,25 @@
+HEADERS += \
+ text/q3syntaxhighlighter.h \
+ text/q3syntaxhighlighter_p.h \
+ text/q3textview.h \
+ text/q3textbrowser.h \
+ text/q3textedit.h \
+ text/q3multilineedit.h \
+ text/q3richtext_p.h \
+ text/q3simplerichtext.h \
+ text/q3stylesheet.h \
+ text/q3textstream.h
+
+SOURCES += \
+ text/q3syntaxhighlighter.cpp \
+ text/q3textview.cpp \
+ text/q3textbrowser.cpp \
+ text/q3textedit.cpp \
+ text/q3multilineedit.cpp \
+ text/q3richtext.cpp \
+ text/q3richtext_p.cpp \
+ text/q3simplerichtext.cpp \
+ text/q3stylesheet.cpp \
+ text/q3textstream.cpp
+
+INCLUDEPATH += ../3rdparty/harfbuzz/src
diff --git a/src/qt3support/tools/q3asciicache.h b/src/qt3support/tools/q3asciicache.h
new file mode 100644
index 0000000..c6fc4a3
--- /dev/null
+++ b/src/qt3support/tools/q3asciicache.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3ASCIICACHE_H
+#define Q3ASCIICACHE_H
+
+#include <Qt3Support/q3gcache.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3AsciiCache
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GCache
+#endif
+{
+public:
+ Q3AsciiCache(const Q3AsciiCache<type> &c) : Q3GCache(c) {}
+ Q3AsciiCache(int maxCost=100, int size=17, bool caseSensitive=true,
+ bool copyKeys=true)
+ : Q3GCache(maxCost, size, AsciiKey, caseSensitive, copyKeys) {}
+ ~Q3AsciiCache() { clear(); }
+ Q3AsciiCache<type> &operator=(const Q3AsciiCache<type> &c)
+ { return (Q3AsciiCache<type>&)Q3GCache::operator=(c); }
+ int maxCost() const { return Q3GCache::maxCost(); }
+ int totalCost() const { return Q3GCache::totalCost(); }
+ void setMaxCost(int m) { Q3GCache::setMaxCost(m); }
+ uint count() const { return Q3GCache::count(); }
+ uint size() const { return Q3GCache::size(); }
+ bool isEmpty() const { return Q3GCache::count() == 0; }
+ void clear() { Q3GCache::clear(); }
+ bool insert(const char *k, const type *d, int c=1, int p=0)
+ { return Q3GCache::insert_other(k,(Item)d,c,p);}
+ bool remove(const char *k)
+ { return Q3GCache::remove_other(k); }
+ type *take(const char *k)
+ { return (type *)Q3GCache::take_other(k); }
+ type *find(const char *k, bool ref=true) const
+ { return (type *)Q3GCache::find_other(k,ref);}
+ type *operator[](const char *k) const
+ { return (type *)Q3GCache::find_other(k);}
+ void statistics() const { Q3GCache::statistics(); }
+private:
+ void deleteItem(Item d);
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3AsciiCache<void>::deleteItem(Q3PtrCollection::Item)
+{
+}
+#endif
+
+template<class type> inline void Q3AsciiCache<type>::deleteItem(Q3PtrCollection::Item d)
+{
+ if (del_item) delete (type *)d;
+}
+
+
+template<class type>
+class Q3AsciiCacheIterator : public Q3GCacheIterator
+{
+public:
+ Q3AsciiCacheIterator(const Q3AsciiCache<type> &c):Q3GCacheIterator((Q3GCache &)c) {}
+ Q3AsciiCacheIterator(const Q3AsciiCacheIterator<type> &ci)
+ : Q3GCacheIterator((Q3GCacheIterator &)ci) {}
+ Q3AsciiCacheIterator<type> &operator=(const Q3AsciiCacheIterator<type>&ci)
+ { return (Q3AsciiCacheIterator<type>&)Q3GCacheIterator::operator=(ci); }
+ uint count() const { return Q3GCacheIterator::count(); }
+ bool isEmpty() const { return Q3GCacheIterator::count() == 0; }
+ bool atFirst() const { return Q3GCacheIterator::atFirst(); }
+ bool atLast() const { return Q3GCacheIterator::atLast(); }
+ type *toFirst() { return (type *)Q3GCacheIterator::toFirst(); }
+ type *toLast() { return (type *)Q3GCacheIterator::toLast(); }
+ operator type *() const { return (type *)Q3GCacheIterator::get(); }
+ type *current() const { return (type *)Q3GCacheIterator::get(); }
+ const char *currentKey() const { return Q3GCacheIterator::getKeyAscii(); }
+ type *operator()() { return (type *)Q3GCacheIterator::operator()();}
+ type *operator++() { return (type *)Q3GCacheIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GCacheIterator::operator+=(j);}
+ type *operator--() { return (type *)Q3GCacheIterator::operator--(); }
+ type *operator-=(uint j) { return (type *)Q3GCacheIterator::operator-=(j);}
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3ASCIICACHE_H
diff --git a/src/qt3support/tools/q3asciicache.qdoc b/src/qt3support/tools/q3asciicache.qdoc
new file mode 100644
index 0000000..b7c1fb1
--- /dev/null
+++ b/src/qt3support/tools/q3asciicache.qdoc
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3AsciiCache
+ \brief The Q3AsciiCache class is a template class that provides a cache based on char* keys.
+ \compat
+
+ Q3AsciiCache is implemented as a template class. Define a template
+ instance Q3AsciiCache\<X\> to create a cache that operates on
+ pointers to X (X*).
+
+ A cache is a least recently used (LRU) list of cache items. The
+ cache items are accessed via \c char* keys. For Unicode keys use
+ the Q3Cache template instead, which uses QString keys. A Q3Cache
+ has the same performace as a Q3AsciiCache.
+
+ Each cache item has a cost. The sum of item costs, totalCost(),
+ will not exceed the maximum cache cost, maxCost(). If inserting a
+ new item would cause the total cost to exceed the maximum cost,
+ the least recently used items in the cache are removed.
+
+ Apart from insert(), by far the most important function is find()
+ (which also exists as operator[]()). This function looks up an
+ item, returns it, and by default marks it as being the most
+ recently used item.
+
+ There are also methods to remove() or take() an object from the
+ cache. Calling \link Q3PtrCollection::setAutoDelete()
+ setAutoDelete(TRUE)\endlink tells the cache to delete items that
+ are removed. The default is to not delete items when then are
+ removed (i.e., remove() and take() are equivalent).
+
+ When inserting an item into the cache, only the pointer is copied,
+ not the item itself. This is called a shallow copy. It is possible
+ to make the cache copy all of the item's data (known as a deep
+ copy) when an item is inserted. insert() calls the virtual
+ function Q3PtrCollection::newItem() for the item to be inserted.
+ Inherit a cache and reimplement newItem() if you want deep copies.
+
+ When removing a cache item the virtual function
+ Q3PtrCollection::deleteItem() is called. Its default implementation
+ in Q3AsciiCache is to delete the item if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ There is a Q3AsciiCacheIterator which may be used to traverse the
+ items in the cache in arbitrary order.
+
+ \sa Q3AsciiCacheIterator, Q3Cache, Q3IntCache
+*/
+
+/*!
+ \fn Q3AsciiCache::Q3AsciiCache( const Q3AsciiCache<type> &c )
+
+ \internal
+
+ Do not use. A Q3AsciiCache cannot be copied. Calls qFatal() in debug version.
+*/
+
+
+/*!
+ \fn Q3AsciiCache::Q3AsciiCache( int maxCost, int size, bool caseSensitive, bool copyKeys )
+
+ Constructs a cache whose contents will never have a total cost
+ greater than \a maxCost and which is expected to contain less than
+ \a size items.
+
+ \a size is actually the size of an internal hash array; it's
+ usually best to make it prime and at least 50% bigger than the
+ largest expected number of items in the cache.
+
+ Each inserted item has an associated cost. When inserting a new
+ item, if the total cost of all items in the cache will exceed \a
+ maxCost, the cache will start throwing out the older (least
+ recently used) items until there is enough room for the new item
+ to be inserted.
+
+ If \a caseSensitive is TRUE (the default), the cache keys are case
+ sensitive; if it is FALSE, they are case-insensitive.
+ Case-insensitive comparison only affects the 26 letters in
+ US-ASCII. If \a copyKeys is TRUE (the default), Q3AsciiCache makes
+ a copy of the cache keys, otherwise it copies just the const char
+ * pointer - slightly faster if you can guarantee that the keys
+ will never change, but very risky.
+*/
+
+/*!
+ \fn Q3AsciiCache::~Q3AsciiCache()
+
+ Removes all items from the cache and destroys it.
+ All iterators that access this cache will be reset.
+*/
+
+/*!
+ \fn Q3AsciiCache<type>& Q3AsciiCache::operator=( const Q3AsciiCache<type> &c )
+
+ \internal
+
+ Do not use. A Q3AsciiCache cannot be copied. Calls qFatal() in debug version.
+*/
+
+/*!
+ \fn int Q3AsciiCache::maxCost() const
+
+ Returns the maximum allowed total cost of the cache.
+
+ \sa setMaxCost() totalCost()
+*/
+
+/*!
+ \fn int Q3AsciiCache::totalCost() const
+
+ Returns the total cost of the items in the cache. This is an
+ integer in the range 0 to maxCost().
+
+ \sa setMaxCost()
+*/
+
+/*!
+ \fn void Q3AsciiCache::setMaxCost( int m )
+
+ Sets the maximum allowed total cost of the cache to \a m. If the
+ current total cost is greater than \a m, some items are removed
+ immediately.
+
+ \sa maxCost() totalCost()
+*/
+
+/*!
+ \fn uint Q3AsciiCache::count() const
+
+ Returns the number of items in the cache.
+
+ \sa totalCost() size()
+*/
+
+/*!
+ \fn uint Q3AsciiCache::size() const
+
+ Returns the size of the hash array used to implement the cache.
+ This should be a bit bigger than count() is likely to be.
+*/
+
+/*!
+ \fn bool Q3AsciiCache::isEmpty() const
+
+ Returns TRUE if the cache is empty; otherwise returns FALSE.
+*/
+
+/*!
+ \fn bool Q3AsciiCache::insert( const char *k, const type *d, int c, int p )
+
+ Inserts the item \a d into the cache using key \a k, and with an
+ associated cost of \a c. Returns TRUE if the item is successfully
+ inserted. Returns FALSE if the item is not inserted, for example,
+ if the cost of the item exceeds maxCost().
+
+ The cache's size is limited, and if the total cost is too high,
+ Q3AsciiCache will remove old, least recently used items until there
+ is room for this new item.
+
+ Items with duplicate keys can be inserted.
+
+ The parameter \a p is internal and should be left at the default
+ value (0).
+
+ \warning If this function returns FALSE, you must delete \a d
+ yourself. Additionally, be very careful about using \a d after
+ calling this function, because any other insertions into the
+ cache, from anywhere in the application or within Qt itself, could
+ cause the object to be discarded from the cache and the pointer to
+ become invalid.
+*/
+
+/*!
+ \fn bool Q3AsciiCache::remove( const char *k )
+
+ Removes the item with key \a k and returns TRUE if the item was
+ present in the cache; otherwise returns FALSE.
+
+ The item is deleted if auto-deletion has been enabled, i.e., if
+ you have called \link Q3PtrCollection::setAutoDelete()
+ setAutoDelete(TRUE)\endlink.
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is removed.
+
+ All iterators that refer to the removed item are set to point to
+ the next item in the cache's traversal order.
+
+ \sa take(), clear()
+*/
+
+/*!
+ \fn type *Q3AsciiCache::take( const char *k )
+
+ Takes the item associated with \a k out of the cache without
+ deleting it and returns a pointer to the item taken out, or 0
+ if the key does not exist in the cache.
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is taken.
+
+ All iterators that refer to the taken item are set to point to the
+ next item in the cache's traversal order.
+
+ \sa remove(), clear()
+*/
+
+/*!
+ \fn void Q3AsciiCache::clear()
+
+ Removes all items from the cache, and deletes them if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink has been
+ enabled.
+
+ All cache iterators that operate on this cache are reset.
+
+ \sa remove() take()
+*/
+
+/*!
+ \fn type *Q3AsciiCache::find( const char *k, bool ref ) const
+
+ Returns the item with key \a k, or 0 if the key does not exist
+ in the cache. If \a ref is TRUE (the default), the item is moved
+ to the front of the least recently used list.
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is returned.
+*/
+
+/*!
+ \fn type *Q3AsciiCache::operator[]( const char *k ) const
+
+ Returns the item with key \a k, or 0 if \a k does not exist in
+ the cache, and moves the item to the front of the least recently
+ used list.
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is returned.
+
+ This is the same as find( k, TRUE ).
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3AsciiCache::statistics() const
+
+ A debug-only utility function. Prints out cache usage, hit/miss,
+ and distribution information using qDebug(). This function does
+ nothing in the release library.
+*/
+
+/*!
+ \class Q3AsciiCacheIterator
+ \brief The Q3AsciiCacheIterator class provides an iterator for Q3AsciiCache collections.
+ \compat
+
+ Note that the traversal order is arbitrary; you are not guaranteed
+ any particular order. If new objects are inserted into the cache
+ while the iterator is active, the iterator may or may not see
+ them.
+
+ Multiple iterators are completely independent, even when they
+ operate on the same Q3AsciiCache. Q3AsciiCache updates all iterators
+ that refer an item when that item is removed.
+
+ Q3AsciiCacheIterator provides an operator++() and an operator+=()
+ to traverse the cache; current() and currentKey() to access the
+ current cache item and its key. It also provides atFirst() and
+ atLast(), which return TRUE if the iterator points to the first or
+ last item in the cache respectively. The isEmpty() function
+ returns TRUE if the cache is empty; and count() returns the number
+ of items in the cache.
+
+ Note that atFirst() and atLast() refer to the iterator's arbitrary
+ ordering, not to the cache's internal least recently used list.
+
+ \sa Q3AsciiCache
+*/
+
+/*!
+ \fn Q3AsciiCacheIterator::Q3AsciiCacheIterator( const Q3AsciiCache<type> &cache )
+
+ Constructs an iterator for \a cache. The current iterator item is
+ set to point to the first item in the \a cache.
+*/
+
+/*!
+ \fn Q3AsciiCacheIterator::Q3AsciiCacheIterator (const Q3AsciiCacheIterator<type> & ci)
+
+ Constructs an iterator for the same cache as \a ci. The new
+ iterator starts at the same item as ci.current() but moves
+ independently from there on.
+*/
+
+/*!
+ \fn Q3AsciiCacheIterator<type>& Q3AsciiCacheIterator::operator=( const Q3AsciiCacheIterator<type> &ci )
+
+ Makes this an iterator for the same cache as \a ci. The new
+ iterator starts at the same item as ci.current(), but moves
+ independently thereafter.
+*/
+
+/*!
+ \fn uint Q3AsciiCacheIterator::count() const
+
+ Returns the number of items in the cache over which this iterator
+ operates.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3AsciiCacheIterator::isEmpty() const
+
+ Returns TRUE if the cache is empty, i.e. count() == 0; otherwise
+ returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn bool Q3AsciiCacheIterator::atFirst() const
+
+ Returns TRUE if the iterator points to the first item in the
+ cache; otherwise returns FALSE. Note that this refers to the
+ iterator's arbitrary ordering, not to the cache's internal least
+ recently used list.
+
+ \sa toFirst(), atLast()
+*/
+
+/*!
+ \fn bool Q3AsciiCacheIterator::atLast() const
+
+ Returns TRUE if the iterator points to the last item in the cache;
+ otherwise returns FALSE. Note that this refers to the iterator's
+ arbitrary ordering, not to the cache's internal least recently
+ used list.
+
+ \sa toLast(), atFirst()
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::toFirst()
+
+ Sets the iterator to point to the first item in the cache and
+ returns a pointer to the item.
+
+ Sets the iterator to 0 and returns 0 if the cache is empty.
+
+ \sa toLast() isEmpty()
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::toLast()
+
+ Sets the iterator to point to the last item in the cache and
+ returns a pointer to the item.
+
+ Sets the iterator to 0 and returns 0 if the cache is empty.
+
+ \sa toFirst() isEmpty()
+*/
+
+/*!
+ \fn Q3AsciiCacheIterator::operator type *() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::current() const
+
+ Returns a pointer to the current iterator item.
+*/
+
+/*!
+ \fn const char *Q3AsciiCacheIterator::currentKey() const
+
+ Returns the key for the current iterator item.
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::operator()()
+
+ Makes the succeeding item current and returns the original current
+ item.
+
+ If the current iterator item was the last item in the cache or if
+ it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::operator+=( uint jump )
+
+ Returns the item \a jump positions after the current item, or 0
+ if it is beyond the last item. Makes this the current item.
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::operator-=( uint jump )
+
+ Returns the item \a jump positions before the current item, or 0
+ if it is before the first item. Makes this the current item.
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::operator++()
+
+ Prefix ++ makes the iterator point to the item just after
+ current(), and makes that the new current item for the iterator. If
+ current() was the last item, operator++() returns 0.
+*/
+
+/*!
+ \fn type *Q3AsciiCacheIterator::operator--()
+
+ Prefix -- makes the iterator point to the item just before
+ current(), and makes that the new current item for the iterator. If
+ current() was the first item, operator--() returns 0.
+*/
+
diff --git a/src/qt3support/tools/q3asciidict.h b/src/qt3support/tools/q3asciidict.h
new file mode 100644
index 0000000..4ae2ecb
--- /dev/null
+++ b/src/qt3support/tools/q3asciidict.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3ASCIIDICT_H
+#define Q3ASCIIDICT_H
+
+#include <Qt3Support/q3gdict.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3AsciiDict
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GDict
+#endif
+{
+public:
+ Q3AsciiDict(int size=17, bool caseSensitive=true, bool copyKeys=true)
+ : Q3GDict(size,AsciiKey,caseSensitive,copyKeys) {}
+ Q3AsciiDict(const Q3AsciiDict<type> &d) : Q3GDict(d) {}
+ ~Q3AsciiDict() { clear(); }
+ Q3AsciiDict<type> &operator=(const Q3AsciiDict<type> &d)
+ { return (Q3AsciiDict<type>&)Q3GDict::operator=(d); }
+ uint count() const { return Q3GDict::count(); }
+ uint size() const { return Q3GDict::size(); }
+ bool isEmpty() const { return Q3GDict::count() == 0; }
+
+ void insert(const char *k, const type *d)
+ { Q3GDict::look_ascii(k,(Item)d,1); }
+ void replace(const char *k, const type *d)
+ { Q3GDict::look_ascii(k,(Item)d,2); }
+ bool remove(const char *k) { return Q3GDict::remove_ascii(k); }
+ type *take(const char *k) { return (type *)Q3GDict::take_ascii(k); }
+ type *find(const char *k) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_ascii(k,0,0); }
+ type *operator[](const char *k) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_ascii(k,0,0); }
+
+ void clear() { Q3GDict::clear(); }
+ void resize(uint n) { Q3GDict::resize(n); }
+ void statistics() const { Q3GDict::statistics(); }
+
+#ifdef qdoc
+protected:
+ virtual QDataStream& read(QDataStream &, Q3PtrCollection::Item &);
+ virtual QDataStream& write(QDataStream &, Q3PtrCollection::Item) const;
+#endif
+
+private:
+ void deleteItem(Item d);
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3AsciiDict<void>::deleteItem(Q3PtrCollection::Item)
+{
+}
+#endif
+
+template<class type> inline void Q3AsciiDict<type>::deleteItem(Q3PtrCollection::Item d)
+{
+ if (del_item) delete (type *)d;
+}
+
+template<class type>
+class Q3AsciiDictIterator : public Q3GDictIterator
+{
+public:
+ Q3AsciiDictIterator(const Q3AsciiDict<type> &d)
+ : Q3GDictIterator((Q3GDict &)d) {}
+ ~Q3AsciiDictIterator() {}
+ uint count() const { return dict->count(); }
+ bool isEmpty() const { return dict->count() == 0; }
+ type *toFirst() { return (type *)Q3GDictIterator::toFirst(); }
+ operator type *() const { return (type *)Q3GDictIterator::get(); }
+ type *current() const { return (type *)Q3GDictIterator::get(); }
+ const char *currentKey() const { return Q3GDictIterator::getKeyAscii(); }
+ type *operator()() { return (type *)Q3GDictIterator::operator()(); }
+ type *operator++() { return (type *)Q3GDictIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GDictIterator::operator+=(j);}
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3ASCIIDICT_H
diff --git a/src/qt3support/tools/q3asciidict.qdoc b/src/qt3support/tools/q3asciidict.qdoc
new file mode 100644
index 0000000..c276682
--- /dev/null
+++ b/src/qt3support/tools/q3asciidict.qdoc
@@ -0,0 +1,402 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3AsciiDict
+ \brief The Q3AsciiDict class is a template class that provides a dictionary based on char* keys.
+ \compat
+
+ Q3AsciiDict is implemented as a template class. Define a template
+ instance Q3AsciiDict\<X\> to create a dictionary that operates on
+ pointers to X (X*).
+
+ A dictionary is a collection of key-value pairs. The key is a
+ char* used for insertion, removal and lookup. The value is a
+ pointer. Dictionaries provide very fast insertion and lookup.
+
+ Q3AsciiDict cannot handle Unicode keys; use the Q3Dict template
+ instead, which uses QString keys. A Q3Dict has the same
+ performace as a Q3AsciiDict.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3asciidict.cpp 0
+ In this example we use a dictionary to keep track of the line
+ edits we're using. We insert each line edit into the dictionary
+ with a unique name and then access the line edits via the
+ dictionary. See Q3PtrDict, Q3IntDict and Q3Dict.
+
+ See Q3Dict for full details, including the choice of dictionary
+ size, and how deletions are handled.
+
+ \sa Q3AsciiDictIterator, Q3Dict, Q3IntDict, Q3PtrDict
+*/
+
+
+/*!
+ \fn Q3AsciiDict::Q3AsciiDict( int size, bool caseSensitive, bool copyKeys )
+
+ Constructs a dictionary optimized for less than \a size entries.
+
+ We recommend setting \a size to a suitably large prime number (a
+ bit larger than the expected number of entries). This makes the
+ hash distribution better and will improve lookup performance.
+
+ When \a caseSensitive is TRUE (the default) Q3AsciiDict treats
+ "abc" and "Abc" as different keys; when it is FALSE "abc" and
+ "Abc" are the same. Case-insensitive comparison only considers the
+ 26 letters in US-ASCII.
+
+ If \a copyKeys is TRUE (the default), the dictionary copies keys
+ using strcpy(); if it is FALSE, the dictionary just copies the
+ pointers.
+*/
+
+/*!
+ \fn Q3AsciiDict::Q3AsciiDict( const Q3AsciiDict<type> &dict )
+
+ Constructs a copy of \a dict.
+
+ Each item in \a dict is inserted into this dictionary. Only the
+ pointers are copied (shallow copy).
+*/
+
+/*!
+ \fn Q3AsciiDict::~Q3AsciiDict()
+
+ Removes all items from the dictionary and destroys it.
+
+ The items are deleted if auto-delete is enabled.
+
+ All iterators that access this dictionary will be reset.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn Q3AsciiDict<type> &Q3AsciiDict::operator=(const Q3AsciiDict<type> &dict)
+
+ Assigns \a dict to this dictionary and returns a reference to this
+ dictionary.
+
+ This dictionary is first cleared and then each item in \a dict is
+ inserted into this dictionary. Only the pointers are copied
+ (shallow copy) unless newItem() has been reimplemented().
+*/
+
+/*!
+ \fn uint Q3AsciiDict::count() const
+
+ Returns the number of items in the dictionary.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn uint Q3AsciiDict::size() const
+
+ Returns the size of the internal hash array (as specified in the
+ constructor).
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3AsciiDict::resize( uint newsize )
+
+ Changes the size of the hashtable to \a newsize. The contents of
+ the dictionary are preserved but all iterators on the dictionary
+ become invalid.
+*/
+
+/*!
+ \fn bool Q3AsciiDict::isEmpty() const
+
+ Returns TRUE if the dictionary is empty, i.e. count() == 0;
+ otherwise it returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3AsciiDict::insert( const char *key, const type *item )
+
+ Inserts the \a key with the \a item into the dictionary.
+
+ Multiple items can have the same key, in which case only the last
+ item will be accessible using \l operator[]().
+
+ \a item may not be 0.
+
+ \sa replace()
+*/
+
+/*!
+ \fn void Q3AsciiDict::replace( const char *key, const type *item )
+
+ Replaces an item that has a key equal to \a key with \a item.
+
+ If the item does not already exist, it will be inserted.
+
+ \a item may not be 0.
+
+ Equivalent to:
+ \snippet doc/src/snippets/code/doc_src_q3asciidict.cpp 1
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be replaced.
+
+ \sa insert()
+*/
+
+/*!
+ \fn bool Q3AsciiDict::remove( const char *key )
+
+ Removes the item associated with \a key from the dictionary.
+ Returns TRUE if successful, i.e. if the key existed in the
+ dictionary; otherwise returns FALSE.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be removed.
+
+ The removed item is deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that refer to the removed item will be
+ set to point to the next item in the dictionary traversal order.
+
+ \sa take(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3AsciiDict::take( const char *key )
+
+ Takes the item associated with \a key out of the dictionary
+ without deleting it (even if \link Q3PtrCollection::setAutoDelete()
+ auto-deletion\endlink is enabled).
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be taken.
+
+ Returns a pointer to the item taken out, or 0 if the key does not
+ exist in the dictionary.
+
+ All dictionary iterators that refer to the taken item will be set
+ to point to the next item in the dictionary traversal order.
+
+ \sa remove(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn void Q3AsciiDict::clear()
+
+ Removes all items from the dictionary.
+
+ The removed items are deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that operate on dictionary are reset.
+
+ \sa remove(), take(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3AsciiDict::find( const char *key ) const
+
+ Returns the item associated with \a key, or 0 if the key does not
+ exist in the dictionary.
+
+ This function uses an internal hashing algorithm to optimize
+ lookup.
+
+ If there are two or more items with equal keys, then the item that
+ was most recently inserted will be found.
+
+ Equivalent to the [] operator.
+
+ \sa operator[]()
+*/
+
+/*!
+ \fn type *Q3AsciiDict::operator[]( const char *key ) const
+
+ Returns the item associated with \a key, or 0 if the key does
+ not exist in the dictionary.
+
+ This function uses an internal hashing algorithm to optimize
+ lookup.
+
+ If there are two or more items with equal keys, then the item that
+ was most recently inserted will be found.
+
+ Equivalent to the find() function.
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3AsciiDict::statistics() const
+
+ Debugging-only function that prints out the dictionary
+ distribution using qDebug().
+*/
+
+/*!
+ \fn QDataStream& Q3AsciiDict::read( QDataStream &s,
+ Q3PtrCollection::Item &item )
+
+ Reads a dictionary item from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3AsciiDict::write(QDataStream &s, Q3PtrCollection::Item item) const
+
+ Writes a dictionary \a item to the stream \a s and returns a
+ reference to the stream.
+
+ \sa read()
+*/
+
+/*!
+ \class Q3AsciiDictIterator
+ \brief The Q3AsciiDictIterator class provides an iterator for Q3AsciiDict collections.
+ \compat
+
+ Q3AsciiDictIterator is implemented as a template class. Define a
+ template instance Q3AsciiDictIterator\<X\> to create a dictionary
+ iterator that operates on Q3AsciiDict\<X\> (dictionary of X*).
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3asciidict.cpp 2
+ In the example we insert some line edits into a dictionary, then
+ iterate over the dictionary printing the strings associated with
+ those line edits.
+
+ Note that the traversal order is arbitrary; you are not guaranteed
+ any particular order.
+
+ Multiple iterators may independently traverse the same dictionary.
+ A Q3AsciiDict knows about all the iterators that are operating on
+ the dictionary. When an item is removed from the dictionary,
+ Q3AsciiDict updates all the iterators that are referring to the
+ removed item to point to the next item in the (arbitrary)
+ traversal order.
+
+ \sa Q3AsciiDict
+*/
+
+/*!
+ \fn Q3AsciiDictIterator::Q3AsciiDictIterator( const Q3AsciiDict<type> &dict )
+
+ Constructs an iterator for \a dict. The current iterator item is
+ set to point on the first item in the \a dict.
+*/
+
+/*!
+ \fn Q3AsciiDictIterator::~Q3AsciiDictIterator()
+
+ Destroys the iterator.
+*/
+
+/*!
+ \fn uint Q3AsciiDictIterator::count() const
+
+ Returns the number of items in the dictionary this iterator
+ operates over.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3AsciiDictIterator::isEmpty() const
+
+ Returns TRUE if the dictionary is empty, i.e. count() == 0,
+ otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn type *Q3AsciiDictIterator::toFirst()
+
+ Sets the current iterator item to point to the first item in the
+ dictionary and returns a pointer to the item. If the dictionary is
+ empty it sets the current item to 0 and returns 0.
+*/
+
+/*!
+ \fn Q3AsciiDictIterator::operator type *() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3AsciiDictIterator::current() const
+
+ Returns a pointer to the current iterator item.
+*/
+
+/*!
+ \fn const char *Q3AsciiDictIterator::currentKey() const
+
+ Returns a pointer to the key for the current iterator item.
+*/
+
+/*!
+ \fn type *Q3AsciiDictIterator::operator()()
+
+ Makes the succeeding item current and returns the original current
+ item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3AsciiDictIterator::operator++()
+
+ Prefix ++ makes the succeeding item current and returns the new
+ current item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3AsciiDictIterator::operator+=( uint jump )
+
+ Sets the current item to the item \a jump positions after the
+ current item, and returns a pointer to that item.
+
+ If that item is beyond the last item or if the dictionary is
+ empty, it sets the current item to 0 and returns 0.
+*/
diff --git a/src/qt3support/tools/q3cache.h b/src/qt3support/tools/q3cache.h
new file mode 100644
index 0000000..d02e1f7
--- /dev/null
+++ b/src/qt3support/tools/q3cache.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3CACHE_H
+#define Q3CACHE_H
+
+#include <Qt3Support/q3gcache.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3Cache
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GCache
+#endif
+{
+public:
+ Q3Cache(const Q3Cache<type> &c) : Q3GCache(c) {}
+ Q3Cache(int maxCost=100, int size=17, bool caseSensitive=true)
+ : Q3GCache(maxCost, size, StringKey, caseSensitive, false) {}
+ ~Q3Cache() { clear(); }
+ Q3Cache<type> &operator=(const Q3Cache<type> &c)
+ { return (Q3Cache<type>&)Q3GCache::operator=(c); }
+ int maxCost() const { return Q3GCache::maxCost(); }
+ int totalCost() const { return Q3GCache::totalCost(); }
+ void setMaxCost(int m) { Q3GCache::setMaxCost(m); }
+ uint count() const { return Q3GCache::count(); }
+ uint size() const { return Q3GCache::size(); }
+ bool isEmpty() const { return Q3GCache::count() == 0; }
+ void clear() { Q3GCache::clear(); }
+ bool insert(const QString &k, const type *d, int c=1, int p=0)
+ { return Q3GCache::insert_string(k,(Item)d,c,p);}
+ bool remove(const QString &k)
+ { return Q3GCache::remove_string(k); }
+ type *take(const QString &k)
+ { return (type *)Q3GCache::take_string(k); }
+ type *find(const QString &k, bool ref=true) const
+ { return (type *)Q3GCache::find_string(k,ref);}
+ type *operator[](const QString &k) const
+ { return (type *)Q3GCache::find_string(k);}
+ void statistics() const { Q3GCache::statistics(); }
+private:
+ void deleteItem(Item d);
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3Cache<void>::deleteItem(Q3PtrCollection::Item)
+{
+}
+#endif
+
+template<class type> inline void Q3Cache<type>::deleteItem(Q3PtrCollection::Item d)
+{
+ if (del_item) delete (type *)d;
+}
+
+template<class type>
+class Q3CacheIterator : public Q3GCacheIterator
+{
+public:
+ Q3CacheIterator(const Q3Cache<type> &c):Q3GCacheIterator((Q3GCache &)c) {}
+ Q3CacheIterator(const Q3CacheIterator<type> &ci)
+ : Q3GCacheIterator((Q3GCacheIterator &)ci) {}
+ Q3CacheIterator<type> &operator=(const Q3CacheIterator<type>&ci)
+ { return (Q3CacheIterator<type>&)Q3GCacheIterator::operator=(ci); }
+ uint count() const { return Q3GCacheIterator::count(); }
+ bool isEmpty() const { return Q3GCacheIterator::count() == 0; }
+ bool atFirst() const { return Q3GCacheIterator::atFirst(); }
+ bool atLast() const { return Q3GCacheIterator::atLast(); }
+ type *toFirst() { return (type *)Q3GCacheIterator::toFirst(); }
+ type *toLast() { return (type *)Q3GCacheIterator::toLast(); }
+ operator type *() const { return (type *)Q3GCacheIterator::get(); }
+ type *current() const { return (type *)Q3GCacheIterator::get(); }
+ QString currentKey() const{ return Q3GCacheIterator::getKeyString(); }
+ type *operator()() { return (type *)Q3GCacheIterator::operator()();}
+ type *operator++() { return (type *)Q3GCacheIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GCacheIterator::operator+=(j);}
+ type *operator--() { return (type *)Q3GCacheIterator::operator--(); }
+ type *operator-=(uint j) { return (type *)Q3GCacheIterator::operator-=(j);}
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3CACHE_H
diff --git a/src/qt3support/tools/q3cache.qdoc b/src/qt3support/tools/q3cache.qdoc
new file mode 100644
index 0000000..6364f37
--- /dev/null
+++ b/src/qt3support/tools/q3cache.qdoc
@@ -0,0 +1,447 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3Cache
+ \brief The Q3Cache class is a template class that provides a cache based on QString keys.
+ \compat
+
+ A cache is a least recently used (LRU) list of cache items. Each
+ cache item has a key and a certain cost. The sum of item costs,
+ totalCost(), never exceeds the maximum cache cost, maxCost(). If
+ inserting a new item would cause the total cost to exceed the
+ maximum cost, the least recently used items in the cache are
+ removed.
+
+ Q3Cache is a template class. Q3Cache\<X\> defines a cache that
+ operates on pointers to X, or X*.
+
+ Apart from insert(), by far the most important function is find()
+ (which also exists as operator[]()). This function looks up an
+ item, returns it, and by default marks it as being the most
+ recently used item.
+
+ There are also methods to remove() or take() an object from the
+ cache. Calling setAutoDelete(TRUE) for a cache tells it to delete
+ items that are removed. The default is to not delete items when
+ they are removed (i.e., remove() and take() are equivalent).
+
+ When inserting an item into the cache, only the pointer is copied,
+ not the item itself. This is called a shallow copy. It is possible
+ to make the cache copy all of the item's data (known as a deep
+ copy) when an item is inserted. insert() calls the virtual
+ function Q3PtrCollection::newItem() for the item to be inserted.
+ Inherit a cache and reimplement newItem() if you want deep copies.
+
+ When removing a cache item, the virtual function
+ Q3PtrCollection::deleteItem() is called. The default
+ implementation deletes the item if auto-deletion is enabled, and
+ does nothing otherwise.
+
+ There is a Q3CacheIterator that can be used to traverse the items
+ in the cache in arbitrary order.
+
+ In Q3Cache, the cache items are accessed via QString keys, which
+ are Unicode strings. If you want to use non-Unicode, plain 8-bit
+ \c char* keys, use the Q3AsciiCache template. A Q3Cache has the
+ same performance as a Q3AsciiCache.
+
+ \sa Q3CacheIterator, Q3AsciiCache, Q3IntCache
+*/
+
+/*!
+ \fn Q3Cache::Q3Cache( const Q3Cache<type> &c )
+
+ \internal
+
+ Do not use. A Q3Cache cannot be copied. Calls qFatal() in debug version.
+*/
+
+
+/*!
+ \fn Q3Cache::Q3Cache( int maxCost, int size, bool caseSensitive )
+
+ Constructs a cache whose contents will never have a total cost
+ greater than \a maxCost and which is expected to contain less than
+ \a size items.
+
+ \a size is actually the size of an internal hash array; it's
+ usually best to make it a prime number and at least 50% bigger
+ than the largest expected number of items in the cache.
+
+ Each inserted item has an associated cost. When inserting a new
+ item, if the total cost of all items in the cache will exceed \a
+ maxCost, the cache will start throwing out the older (least
+ recently used) items until there is enough room for the new item
+ to be inserted.
+
+ If \a caseSensitive is TRUE (the default), the cache keys are case
+ sensitive; if it is FALSE, they are case-insensitive.
+ Case-insensitive comparison considers all Unicode letters.
+*/
+
+/*!
+ \fn Q3Cache::~Q3Cache()
+
+ Removes all items from the cache and destroys it. All iterators
+ that access this cache will be reset.
+*/
+
+/*!
+ \fn Q3Cache<type>& Q3Cache::operator=( const Q3Cache<type> &c )
+
+ \internal
+
+ Do not use. A Q3Cache cannot be copied. Calls qFatal() in debug version.
+*/
+
+/*!
+ \fn int Q3Cache::maxCost() const
+
+ Returns the maximum allowed total cost of the cache.
+
+ \sa setMaxCost() totalCost()
+*/
+
+/*!
+ \fn int Q3Cache::totalCost() const
+
+ Returns the total cost of the items in the cache. This is an
+ integer in the range 0 to maxCost().
+
+ \sa setMaxCost()
+*/
+
+/*!
+ \fn void Q3Cache::setMaxCost( int m )
+
+ Sets the maximum allowed total cost of the cache to \a m. If the
+ current total cost is greater than \a m, some items are deleted
+ immediately.
+
+ \sa maxCost() totalCost()
+*/
+
+/*!
+ \fn uint Q3Cache::count() const
+
+ Returns the number of items in the cache.
+
+ \sa totalCost()
+*/
+
+/*!
+ \fn uint Q3Cache::size() const
+
+ Returns the size of the hash array used to implement the cache.
+ This should be a bit bigger than count() is likely to be.
+*/
+
+/*!
+ \fn bool Q3Cache::isEmpty() const
+
+ Returns TRUE if the cache is empty; otherwise returns FALSE.
+*/
+
+/*!
+ \fn bool Q3Cache::insert( const QString &k, const type *d, int c, int p )
+
+ Inserts the item \a d into the cache with key \a k and associated
+ cost, \a c. Returns TRUE if it is successfully inserted; otherwise
+ returns FALSE.
+
+ The cache's size is limited, and if the total cost is too high,
+ Q3Cache will remove old, least recently used items until there is
+ room for this new item.
+
+ The parameter \a p is internal and should be left at the default
+ value (0).
+
+ \warning If this function returns FALSE (which could happen, e.g.
+ if the cost of this item alone exceeds maxCost()) you must delete
+ \a d yourself. Additionally, be very careful about using \a d
+ after calling this function because any other insertions into the
+ cache, from anywhere in the application or within Qt itself, could
+ cause the object to be discarded from the cache and the pointer to
+ become invalid.
+*/
+
+/*!
+ \fn bool Q3Cache::remove( const QString &k )
+
+ Removes the item associated with \a k, and returns TRUE if the
+ item was present in the cache; otherwise returns FALSE.
+
+ The item is deleted if auto-deletion has been enabled, i.e., if
+ you have called setAutoDelete(TRUE).
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is removed.
+
+ All iterators that refer to the removed item are set to point to
+ the next item in the cache's traversal order.
+
+ \sa take(), clear()
+*/
+
+/*!
+ \fn type *Q3Cache::take( const QString &k )
+
+ Takes the item associated with \a k out of the cache without
+ deleting it, and returns a pointer to the item taken out, or 0
+ if the key does not exist in the cache.
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is taken.
+
+ All iterators that refer to the taken item are set to point to the
+ next item in the cache's traversal order.
+
+ \sa remove(), clear()
+*/
+
+/*!
+ \fn void Q3Cache::clear()
+
+ Removes all items from the cache and deletes them if auto-deletion
+ has been enabled.
+
+ All cache iterators that operate this on cache are reset.
+
+ \sa remove() take()
+*/
+
+/*!
+ \fn type *Q3Cache::find( const QString &k, bool ref ) const
+
+ Returns the item associated with key \a k, or 0 if the key does
+ not exist in the cache. If \a ref is TRUE (the default), the item
+ is moved to the front of the least recently used list.
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is returned.
+*/
+
+/*!
+ \fn type *Q3Cache::operator[]( const QString &k ) const
+
+ Returns the item associated with key \a k, or 0 if \a k does not
+ exist in the cache, and moves the item to the front of the least
+ recently used list.
+
+ If there are two or more items with equal keys, the one that was
+ inserted last is returned.
+
+ This is the same as find( k, TRUE ).
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3Cache::statistics() const
+
+ A debug-only utility function. Prints out cache usage, hit/miss,
+ and distribution information using qDebug(). This function does
+ nothing in the release library.
+*/
+
+/*****************************************************************************
+ Q3CacheIterator documentation
+ *****************************************************************************/
+
+/*!
+ \class Q3CacheIterator qcache.h
+ \brief The Q3CacheIterator class provides an iterator for Q3Cache collections.
+ \compat
+
+ Note that the traversal order is arbitrary; you are not guaranteed
+ any particular order. If new objects are inserted into the cache
+ while the iterator is active, the iterator may or may not see
+ them.
+
+ Multiple iterators are completely independent, even when they
+ operate on the same Q3Cache. Q3Cache updates all iterators that
+ refer an item when that item is removed.
+
+ Q3CacheIterator provides an operator++(), and an operator+=() to
+ traverse the cache. The current() and currentKey() functions are
+ used to access the current cache item and its key. The atFirst()
+ and atLast() return TRUE if the iterator points to the first or
+ last item in the cache respectively. The isEmpty() function
+ returns TRUE if the cache is empty, and count() returns the number
+ of items in the cache.
+
+ Note that atFirst() and atLast() refer to the iterator's arbitrary
+ ordering, not to the cache's internal least recently used list.
+
+ \sa Q3Cache
+*/
+
+/*!
+ \fn Q3CacheIterator::Q3CacheIterator( const Q3Cache<type> &cache )
+
+ Constructs an iterator for \a cache. The current iterator item is
+ set to point to the first item in the \a cache.
+*/
+
+/*!
+ \fn Q3CacheIterator::Q3CacheIterator (const Q3CacheIterator<type> & ci)
+
+ Constructs an iterator for the same cache as \a ci. The new
+ iterator starts at the same item as ci.current(), but moves
+ independently from there on.
+*/
+
+/*!
+ \fn Q3CacheIterator<type>& Q3CacheIterator::operator=( const Q3CacheIterator<type> &ci )
+
+ Makes this an iterator for the same cache as \a ci. The new
+ iterator starts at the same item as ci.current(), but moves
+ independently thereafter.
+*/
+
+/*!
+ \fn uint Q3CacheIterator::count() const
+
+ Returns the number of items in the cache on which this iterator
+ operates.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3CacheIterator::isEmpty() const
+
+ Returns TRUE if the cache is empty, i.e. count() == 0; otherwise
+ it returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn bool Q3CacheIterator::atFirst() const
+
+ Returns TRUE if the iterator points to the first item in the
+ cache; otherwise returns FALSE. Note that this refers to the
+ iterator's arbitrary ordering, not to the cache's internal least
+ recently used list.
+
+ \sa toFirst(), atLast()
+*/
+
+/*!
+ \fn bool Q3CacheIterator::atLast() const
+
+ Returns TRUE if the iterator points to the last item in the cache;
+ otherwise returns FALSE. Note that this refers to the iterator's
+ arbitrary ordering, not to the cache's internal least recently
+ used list.
+
+ \sa toLast(), atFirst()
+*/
+
+/*!
+ \fn type *Q3CacheIterator::toFirst()
+
+ Sets the iterator to point to the first item in the cache and
+ returns a pointer to the item.
+
+ Sets the iterator to 0 and returns 0 if the cache is empty.
+
+ \sa toLast() isEmpty()
+*/
+
+/*!
+ \fn type *Q3CacheIterator::toLast()
+
+ Sets the iterator to point to the last item in the cache and
+ returns a pointer to the item.
+
+ Sets the iterator to 0 and returns 0 if the cache is empty.
+
+ \sa toFirst() isEmpty()
+*/
+
+/*!
+ \fn Q3CacheIterator::operator type *() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3CacheIterator::current() const
+
+ Returns a pointer to the current iterator item.
+*/
+
+/*!
+ \fn QString Q3CacheIterator::currentKey() const
+
+ Returns the key for the current iterator item.
+*/
+
+/*!
+ \fn type *Q3CacheIterator::operator()()
+
+ Makes the succeeding item current and returns the original current
+ item.
+
+ If the current iterator item was the last item in the cache or if
+ it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3CacheIterator::operator+=( uint jump )
+
+ Returns the item \a jump positions after the current item, or 0 if
+ it is beyond the last item. Makes this the current item.
+*/
+
+/*!
+ \fn type *Q3CacheIterator::operator-=( uint jump )
+
+ Returns the item \a jump positions before the current item, or 0
+ if it is before the first item. Makes this the current item.
+*/
+
+/*!
+ \fn type *Q3CacheIterator::operator++()
+
+ Prefix++ makes the iterator point to the item just after current()
+ and makes that the new current item for the iterator. If current()
+ was the last item, operator++() returns 0.
+*/
+
+/*!
+ \fn type *Q3CacheIterator::operator--()
+
+ Prefix-- makes the iterator point to the item just before
+ current() and makes that the new current item for the iterator. If
+ current() was the first item, operator--() returns 0.
+*/
+
diff --git a/src/qt3support/tools/q3cleanuphandler.h b/src/qt3support/tools/q3cleanuphandler.h
new file mode 100644
index 0000000..01dc07d
--- /dev/null
+++ b/src/qt3support/tools/q3cleanuphandler.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3CLEANUPHANDLER_H
+#define Q3CLEANUPHANDLER_H
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class T>
+class Q3CleanupHandler
+{
+ QListData p;
+public:
+ inline Q3CleanupHandler()
+ { p.d = 0; }
+ ~Q3CleanupHandler()
+ {
+ if (p.d) {
+ for (int i = 0; i < p.size(); ++i) {
+ T** t = static_cast<T**>(*p.at(i));
+ delete *t;
+ *t = 0;
+ }
+ qFree(p.d);
+ p.d = 0;
+ }
+ }
+
+ T* add(T **object)
+ {
+ if (!p.d) {
+ p.d = &QListData::shared_null;
+ p.d->ref.ref();
+ p.detach();
+ }
+ *p.prepend() = object;
+ return *object;
+ }
+ void remove(T **object)
+ {
+ if (p.d)
+ for (int i = 0; i < p.size(); ++i)
+ if (*p.at(i) == object)
+ p.remove(i--);
+ }
+};
+
+template<class T>
+class Q3SingleCleanupHandler
+{
+ T **object;
+public:
+ inline Q3SingleCleanupHandler()
+ : object(0) {}
+ inline ~Q3SingleCleanupHandler()
+ { if (object) { delete *object; *object = 0; } }
+ inline T* set(T **o)
+ { object = o; return *object; }
+ inline void reset() { object = 0; }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //Q3CLEANUPHANDLER_H
diff --git a/src/qt3support/tools/q3cstring.cpp b/src/qt3support/tools/q3cstring.cpp
new file mode 100644
index 0000000..d34e30c
--- /dev/null
+++ b/src/qt3support/tools/q3cstring.cpp
@@ -0,0 +1,1072 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3cstring.h"
+#include "qregexp.h"
+#include "qdatastream.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Q3CString member functions
+ *****************************************************************************/
+
+/*!
+ \class Q3CString
+ \reentrant
+ \brief The Q3CString class provides an abstraction of the classic C
+ zero-terminated char array (char *).
+
+ \compat
+
+ Q3CString tries to behave like a more convenient \c{const char *}.
+ The price of doing this is that some algorithms will perform
+ badly. For example, append() is O(length()) since it scans for a
+ null terminator. Although you might use Q3CString for text that is
+ never exposed to the user, for most purposes, and especially for
+ user-visible text, you should use QString. QString provides
+ implicit sharing, Unicode and other internationalization support,
+ and is well optimized.
+
+ Note that for the Q3CString methods that take a \c{const char *}
+ parameter the \c{const char *} must either be 0 (null) or not-null
+ and '\0' (NUL byte) terminated; otherwise the results are
+ undefined.
+
+ A default constructed Q3CString is \e null, i.e. both the length
+ and the data pointer are 0 and isNull() returns true.
+
+ \note However, if you ask for the data pointer of a null Q3CString
+ by calling data(), then because the internal representation of the
+ null Q3CString is shared, it will be detached and replaced with a
+ non-shared, empty representation, a non-null data pointer will be
+ returned, and subsequent calls to isNull() will return false. But
+ if you ask for the data pointer of a null Q3CString by calling
+ constData(), the shared internal representation is not detached, a
+ null data pointer is returned, and subsequent calls to isNull()
+ will continue to return true.
+
+ A Q3CString that references the empty string ("", a single '\0'
+ char) is \e empty, i.e. isEmpty() returns true. Both null and
+ empty Q3CStrings are legal parameters to the methods. Assigning
+ \c{const char *} 0 to Q3CString produces a null Q3CString.
+
+ The length() function returns the length of the string; resize()
+ resizes the string and truncate() truncates the string. A string
+ can be filled with a character using fill(). Strings can be left
+ or right padded with characters using leftJustify() and
+ rightJustify(). Characters, strings and regular expressions can be
+ searched for using find() and findRev(), and counted using
+ contains().
+
+ Strings and characters can be inserted with insert() and appended
+ with append(). A string can be prepended with prepend().
+ Characters can be removed from the string with remove() and
+ replaced with replace().
+
+ Portions of a string can be extracted using left(), right() and
+ mid(). Whitespace can be removed using stripWhiteSpace() and
+ simplifyWhiteSpace(). Strings can be converted to uppercase or
+ lowercase with upper() and lower() respectively.
+
+ Strings that contain numbers can be converted to numbers with
+ toShort(), toInt(), toLong(), toULong(), toFloat() and toDouble().
+ Numbers can be converted to strings with setNum().
+
+ Many operators are overloaded to work with Q3CStrings. Q3CString
+ also supports some more obscure functions, e.g. sprintf(),
+ setStr() and setExpand().
+
+ \sidebar Note on Character Comparisons
+
+ In Q3CString the notion of uppercase and lowercase and of which
+ character is greater than or less than another character is locale
+ dependent. This affects functions which support a case insensitive
+ option or which compare or lowercase or uppercase their arguments.
+ Case insensitive operations and comparisons will be accurate if
+ both strings contain only ASCII characters. (If \c $LC_CTYPE is
+ set, most Unix systems do "the right thing".) Functions that this
+ affects include contains(), find(), findRev(), \l operator<(), \l
+ operator<=(), \l operator>(), \l operator>=(), lower() and
+ upper().
+
+ This issue does not apply to \l{QString}s since they represent
+ characters using Unicode.
+ \endsidebar
+
+ Performance note: The Q3CString methods for QRegExp searching are
+ implemented by converting the Q3CString to a QString and performing
+ the search on that. This implies a deep copy of the Q3CString data.
+ If you are going to perform many QRegExp searches on a large
+ Q3CString, you will get better performance by converting the
+ Q3CString to a QString yourself, and then searching in the QString.
+*/
+
+/*!
+ \fn Q3CString Q3CString::left(uint len) const
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString Q3CString::right(uint len) const
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString Q3CString::mid(uint index, uint len) const
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString Q3CString::lower() const
+
+ Use QByteArray::toLower() instead.
+*/
+
+/*!
+ \fn Q3CString Q3CString::upper() const
+
+ Use QByteArray::toUpper() instead.
+*/
+
+/*!
+ \fn Q3CString Q3CString::stripWhiteSpace() const
+
+ Use QByteArray::trimmed() instead.
+*/
+
+/*!
+ \fn Q3CString Q3CString::simplifyWhiteSpace() const
+
+ Use QByteArray::simplified() instead.
+*/
+
+/*!
+ \fn Q3CString& Q3CString::insert(uint index, const char *c)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::insert(uint index, char c)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::prepend(const char *c)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::remove(uint index, uint len)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::replace(uint index, uint len, const char *c)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::replace(char c, const Q3CString &after)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::replace(char c, const char *after)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::replace(const Q3CString &b, const Q3CString &a)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::replace(const char *b, const char *a)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString& Q3CString::replace(char b, char a)
+
+ \internal
+*/
+
+/*!
+ \fn Q3CString::Q3CString()
+
+ Constructs a null string.
+
+ \sa isNull()
+*/
+
+/*!
+ \fn Q3CString::Q3CString(const QByteArray &ba)
+
+ Constructs a copy of \a ba.
+*/
+
+/*!
+ \fn Q3CString::Q3CString(const Q3CString &s)
+
+ Constructs a shallow copy \a s.
+*/
+
+/*! \fn Q3CString::Q3CString(int size)
+ Constructs a string with room for \a size characters, including
+ the '\0'-terminator. Makes a null string if \a size == 0.
+
+ If \a size \> 0, then the first and last characters in the string
+ are initialized to '\0'. All other characters are uninitialized.
+
+ \sa resize(), isNull()
+*/
+
+/*! \fn Q3CString::Q3CString(const char *str)
+ Constructs a string that is a deep copy of \a str.
+
+ If \a str is 0 a null string is created.
+
+ \sa isNull()
+*/
+
+
+/*! \fn Q3CString::Q3CString(const char *str, uint maxsize)
+
+ Constructs a string that is a deep copy of \a str. The copy will
+ be at most \a maxsize bytes long including the '\0'-terminator.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3cstring.cpp 0
+
+ If \a str contains a 0 byte within the first \a maxsize bytes, the
+ resulting Q3CString will be terminated by this 0. If \a str is 0 a
+ null string is created.
+
+ \sa isNull()
+*/
+
+/*!
+ \fn Q3CString &Q3CString::operator=(const QByteArray &ba)
+
+ Assigns byte array \a ba to this Q3CString.
+*/
+
+/*!
+ \fn Q3CString &Q3CString::operator=(const Q3CString &s)
+
+ Assigns a shallow copy of \a s to this string and returns a
+ reference to this string.
+*/
+
+/*!
+ \fn Q3CString &Q3CString::operator=(const char *str)
+ \overload
+
+ Assigns a deep copy of \a str to this string and returns a
+ reference to this string.
+
+ If \a str is 0 a null string is created.
+
+ \sa isNull()
+*/
+
+/*
+ \fn bool Q3CString::isNull() const
+
+ Returns true if the string is null, i.e. if data() == 0; otherwise
+ returns false. A null string is also an empty string.
+
+ \note If you ask for the data pointer of a null Q3CString by
+ calling data(), then because the internal representation of the
+ null Q3CString is shared, it will be detached and replaced with a
+ non-shared, empty representation, a non-null data pointer will be
+ returned, and subsequent calls to isNull() will return false. But
+ if you ask for the data pointer of a null Q3CString by calling
+ constData(), the shared internal representation is not detached, a
+ null data pointer is returned, and subsequent calls to isNull()
+ will continue to return true.
+
+ Example:
+ \snippet doc/src/snippets/code/src.qt3support.tools.q3cstring.cpp 1
+
+ \sa isEmpty(), length(), size()
+*/
+
+/*
+ \fn bool Q3CString::isEmpty() const
+
+ Returns true if the string is empty, i.e. if length() == 0;
+ otherwise returns false. An empty string is not always a null
+ string.
+
+ See example in isNull().
+
+ \sa isNull(), length(), size()
+*/
+
+/*
+ \fn uint Q3CString::length() const
+
+ Returns the length of the string, excluding the '\0'-terminator.
+ Equivalent to calling \c strlen(data()).
+
+ Null strings and empty strings have zero length.
+
+ \sa size(), isNull(), isEmpty()
+*/
+
+/*
+ \fn bool Q3CString::truncate(uint pos)
+
+ Truncates the string at position \a pos.
+
+ Equivalent to calling \c resize(pos+1).
+
+ Example:
+ \snippet doc/src/snippets/code/src.qt3support.tools.q3cstring.cpp 2
+
+ \sa resize()
+*/
+
+
+
+/*!
+ Implemented as a call to the native vsprintf() (see the manual for
+ your C library).
+
+ If the string is shorter than 256 characters, this sprintf() calls
+ resize(256) to decrease the chance of memory corruption. The
+ string is resized back to its actual length before sprintf()
+ returns.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3cstring.cpp 3
+
+ \warning All vsprintf() implementations will write past the end of
+ the target string (*this) if the \a format specification and
+ arguments happen to be longer than the target string, and some
+ will also fail if the target string is longer than some arbitrary
+ implementation limit.
+
+ Giving user-supplied arguments to sprintf() is risky: Sooner or
+ later someone will paste a huge line into your application.
+*/
+
+Q3CString &Q3CString::sprintf(const char *format, ...)
+{
+ detach();
+ va_list ap;
+ va_start(ap, format);
+ if (size() < 256)
+ resize(256); // make string big enough
+ qvsnprintf(data(), size(), format, ap);
+ resize(qstrlen(constData()));
+ va_end(ap);
+ return *this;
+}
+
+
+
+/*!
+ \fn Q3CString Q3CString::copy() const
+
+ Returns a deep copy of this string.
+*/
+
+
+/*!
+ Returns a string of length \a width (plus one for the terminating
+ '\0') that contains this string padded with the \a fill character.
+
+ If the length of the string exceeds \a width and \a truncate is
+ false (the default), then the returned string is a copy of the
+ string. If the length of the string exceeds \a width and \a
+ truncate is true, then the returned string is a left(\a width).
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3cstring.cpp 4
+
+ \sa rightJustify()
+*/
+
+Q3CString Q3CString::leftJustify(uint width, char fill, bool truncate) const
+{
+ Q3CString result;
+ int len = qstrlen(constData());
+ int padlen = width - len;
+ if (padlen > 0) {
+ result.resize(len+padlen);
+ memcpy(result.data(), constData(), len);
+ memset(result.data()+len, fill, padlen);
+ } else {
+ if (truncate)
+ result = left(width);
+ else
+ result = *this;
+ }
+ return result;
+}
+
+/*!
+ Returns a string of length \a width (plus one for the terminating
+ '\0') that contains zero or more of the \a fill character followed
+ by this string.
+
+ If the length of the string exceeds \a width and \a truncate is
+ false (the default), then the returned string is a copy of the
+ string. If the length of the string exceeds \a width and \a
+ truncate is true, then the returned string is a left(\a width).
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3cstring.cpp 5
+
+ \sa leftJustify()
+*/
+
+Q3CString Q3CString::rightJustify(uint width, char fill, bool truncate) const
+{
+ Q3CString result;
+ int len = qstrlen(constData());
+ int padlen = width - len;
+ if (padlen > 0) {
+ result.resize(len+padlen);
+ memset(result.data(), fill, padlen);
+ memcpy(result.data()+padlen, constData(), len);
+ } else {
+ if (truncate)
+ result = left(width);
+ else
+ result = *this;
+ }
+ return result;
+}
+
+/*!
+ Returns the string converted to a \c long value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, or if it has trailing garbage; otherwise *\a ok is set to
+ true.
+*/
+
+long Q3CString::toLong(bool *ok) const
+{
+ const char *p = constData();
+ long val=0;
+ const long max_mult = 214748364;
+ bool is_ok = false;
+ int neg = 0;
+ if (!p)
+ goto bye;
+ while (isspace((uchar) *p)) // skip leading space
+ p++;
+ if (*p == '-') {
+ p++;
+ neg = 1;
+ } else if (*p == '+') {
+ p++;
+ }
+ if (!isdigit((uchar) *p))
+ goto bye;
+ while (isdigit((uchar) *p)) {
+ if (val > max_mult || (val == max_mult && (*p-'0') > 7+neg))
+ goto bye;
+ val = 10*val + (*p++ - '0');
+ }
+ if (neg)
+ val = -val;
+ while (isspace((uchar) *p)) // skip trailing space
+ p++;
+ if (*p == '\0')
+ is_ok = true;
+bye:
+ if (ok)
+ *ok = is_ok;
+ return is_ok ? val : 0;
+}
+
+/*!
+ Returns the string converted to an \c{unsigned long} value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, or if it has trailing garbage; otherwise *\a ok is set to
+ true.
+*/
+
+ulong Q3CString::toULong(bool *ok) const
+{
+ const char *p = constData();
+ ulong val=0;
+ const ulong max_mult = 429496729;
+ bool is_ok = false;
+ if (!p)
+ goto bye;
+ while (isspace((uchar) *p)) // skip leading space
+ p++;
+ if (*p == '+')
+ p++;
+ if (!isdigit((uchar) *p))
+ goto bye;
+ while (isdigit((uchar) *p)) {
+ if (val > max_mult || (val == max_mult && (*p-'0') > 5))
+ goto bye;
+ val = 10*val + (*p++ - '0');
+ }
+ while (isspace((uchar) *p)) // skip trailing space
+ p++;
+ if (*p == '\0')
+ is_ok = true;
+bye:
+ if (ok)
+ *ok = is_ok;
+ return is_ok ? val : 0;
+}
+
+/*!
+ Returns the string converted to a \c{short} value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, is out of range, or if it has trailing garbage; otherwise
+ *\a ok is set to true.
+*/
+
+short Q3CString::toShort(bool *ok) const
+{
+ long v = toLong(ok);
+ if (ok && *ok && (v < -32768 || v > 32767))
+ *ok = false;
+ return (short)v;
+}
+
+/*!
+ Returns the string converted to an \c{unsigned short} value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, is out of range, or if it has trailing garbage; otherwise
+ *\a ok is set to true.
+*/
+
+ushort Q3CString::toUShort(bool *ok) const
+{
+ ulong v = toULong(ok);
+ if (ok && *ok && (v > 65535))
+ *ok = false;
+ return (ushort)v;
+}
+
+
+/*!
+ Returns the string converted to a \c{int} value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, or if it has trailing garbage; otherwise *\a ok is set to
+ true.
+*/
+
+int Q3CString::toInt(bool *ok) const
+{
+ return (int)toLong(ok);
+}
+
+/*!
+ Returns the string converted to an \c{unsigned int} value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, or if it has trailing garbage; otherwise *\a ok is set to
+ true.
+*/
+
+uint Q3CString::toUInt(bool *ok) const
+{
+ return (uint)toULong(ok);
+}
+
+/*!
+ Returns the string converted to a \c{double} value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, or if it has trailing garbage; otherwise *\a ok is set to
+ true.
+*/
+
+double Q3CString::toDouble(bool *ok) const
+{
+ char *end;
+ double val = strtod(constData() ? constData() : "", &end);
+ if (ok)
+ *ok = (constData() && *constData() && (end == 0 || *end == '\0'));
+ return val;
+}
+
+/*!
+ Returns the string converted to a \c{float} value.
+
+ If \a ok is not 0: *\a ok is set to false if the string is not a
+ number, or if it has trailing garbage; otherwise *\a ok is set to
+ true.
+*/
+
+float Q3CString::toFloat(bool *ok) const
+{
+ return (float)toDouble(ok);
+}
+
+
+/*! \fn Q3CString &Q3CString::setStr(const char *str)
+ Makes a deep copy of \a str. Returns a reference to the string.
+*/
+
+/*!
+ \overload
+
+ Sets the string to the string representation of the number \a n
+ and returns a reference to the string.
+*/
+
+Q3CString &Q3CString::setNum(long n)
+{
+ data();
+ char buf[20];
+ register char *p = &buf[19];
+ bool neg;
+ if (n < 0) {
+ neg = true;
+ n = -n;
+ } else {
+ neg = false;
+ }
+ *p = '\0';
+ do {
+ *--p = ((int)(n%10)) + '0';
+ n /= 10;
+ } while (n);
+ if (neg)
+ *--p = '-';
+ *this = p;
+ return *this;
+}
+
+/*!
+ \overload
+
+ Sets the string to the string representation of the number \a n
+ and returns a reference to the string.
+*/
+
+Q3CString &Q3CString::setNum(ulong n)
+{
+ data();
+ char buf[20];
+ register char *p = &buf[19];
+ *p = '\0';
+ do {
+ *--p = ((int)(n%10)) + '0';
+ n /= 10;
+ } while (n);
+ *this = p;
+ return *this;
+}
+
+/*!
+ \fn Q3CString &Q3CString::setNum(int n)
+ \overload
+
+ Sets the string to the string representation of the number \a n
+ and returns a reference to the string.
+*/
+
+/*!
+ \fn Q3CString &Q3CString::setNum(uint n)
+ \overload
+
+ Sets the string to the string representation of the number \a n
+ and returns a reference to the string.
+*/
+
+/*!
+ \fn Q3CString &Q3CString::setNum(short n)
+ \overload
+
+ Sets the string to the string representation of the number \a n
+ and returns a reference to the string.
+*/
+
+/*!
+ \fn Q3CString &Q3CString::setNum(ushort n)
+ \overload
+
+ Sets the string to the string representation of the number \a n
+ and returns a reference to the string.
+*/
+
+/*!
+ Sets the string to the string representation of the number \a n
+ and returns a reference to the string.
+
+ The format of the string representation is specified by the format
+ character \a f, and the precision (number of digits after the
+ decimal point) is specified with \a prec.
+
+ The valid formats for \a f are 'e', 'E', 'f', 'g' and 'G'. The
+ formats are the same as for sprintf(); they are explained in \l
+ QString::arg().
+*/
+
+Q3CString &Q3CString::setNum(double n, char f, int prec)
+{
+#ifndef QT_NO_DEBUG
+ if (!(f=='f' || f=='F' || f=='e' || f=='E' || f=='g' || f=='G'))
+ qWarning("Q3CString::setNum: Invalid format char '%c'", f);
+#endif
+ char format[20];
+ register char *fs = format; // generate format string
+ *fs++ = '%'; // "%.<prec>l<f>"
+ if (prec > 99)
+ prec = 99;
+ *fs++ = '.';
+ if (prec >= 10) {
+ *fs++ = prec / 10 + '0';
+ *fs++ = prec % 10 + '0';
+ } else {
+ *fs++ = prec + '0';
+ }
+ *fs++ = 'l';
+ *fs++ = f;
+ *fs = '\0';
+ return sprintf(format, n);
+}
+
+/*! \fn Q3CString &Q3CString::setNum(float n, char f, int prec)
+ \overload
+*/
+
+/*!
+ Sets the character at position \a index to \a c and expands the
+ string if necessary, padding with spaces.
+
+ Returns false if \a index was out of range and the string could
+ not be expanded; otherwise returns true.
+*/
+
+bool Q3CString::setExpand(uint index, char c)
+{
+ uint oldlen = length();
+ if (index >= oldlen) {
+ resize(index+1);
+ if (index > oldlen)
+ memset(data() + oldlen, ' ', index - oldlen);
+ }
+ *(data() + index) = c;
+ return true;
+}
+
+
+/*
+ \fn Q3CString::operator const char *() const
+
+ Returns the string data.
+*/
+
+
+/*!
+ \fn Q3CString& Q3CString::append(const char *str)
+
+ Appends string \a str to the string and returns a reference to the
+ string. Equivalent to operator+=().
+*/
+
+
+
+#ifndef QT_NO_DATASTREAM
+/*! \fn QDataStream &operator<<(QDataStream &s, const Q3CString &str)
+ \relates Q3CString
+
+ Writes string \a str to the stream \a s.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator<<(QDataStream &d, const Q3CString &s)
+{
+ if (d.version() >= QDataStream::Qt_4_0)
+ return operator<<(d, static_cast<const QByteArray &>(s));
+
+ // we need to add a NUL to keep compatibility with Qt 3's QByteArray
+ QByteArray copy = s;
+ copy.append('\0');
+ return operator<<(d, copy);
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &s, Q3CString &str)
+ \relates Q3CString
+
+ Reads a string into \a str from the stream \a s.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator>>(QDataStream &d, Q3CString &s) {
+ operator>>(d, static_cast<QByteArray &>(s));
+ if (d.version() < QDataStream::Qt_4_0 && s.endsWith('\0'))
+ s.chop(1); // ending NUL
+ return d;
+}
+#endif
+
+/*****************************************************************************
+ Documentation for related functions
+ *****************************************************************************/
+
+/*!
+ \fn bool operator==(const Q3CString &s1, const Q3CString &s2)
+
+ \relates Q3CString
+
+ Returns true if \a s1 and \a s2 are equal; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) == 0.
+*/
+
+/*!
+ \fn bool operator==(const Q3CString &s1, const char *s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 and \a s2 are equal; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) == 0.
+*/
+
+/*!
+ \fn bool operator==(const char *s1, const Q3CString &s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 and \a s2 are equal; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) == 0.
+*/
+
+/*!
+ \fn bool operator!=(const Q3CString &s1, const Q3CString &s2)
+
+ \relates Q3CString
+
+ Returns true if \a s1 and \a s2 are different; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) != 0.
+*/
+
+/*!
+ \fn bool operator!=(const Q3CString &s1, const char *s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 and \a s2 are different; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) != 0.
+*/
+
+/*!
+ \fn bool operator!=(const char *s1, const Q3CString &s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 and \a s2 are different; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) != 0.
+*/
+
+/*!
+ \fn bool operator<(const Q3CString &s1, const char *s2)
+
+ \relates Q3CString
+
+ Returns true if \a s1 is less than \a s2; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \< 0.
+*/
+
+/*!
+ \fn bool operator<(const char *s1, const Q3CString &s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 is less than \a s2; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \< 0.
+*/
+
+/*!
+ \fn bool operator<=(const Q3CString &s1, const char *s2)
+
+ \relates Q3CString
+
+ Returns true if \a s1 is less than or equal to \a s2; otherwise
+ returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \<= 0.
+*/
+
+/*!
+ \fn bool operator<=(const char *s1, const Q3CString &s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 is less than or equal to \a s2; otherwise
+ returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \<= 0.
+*/
+
+/*!
+ \fn bool operator>(const Q3CString &s1, const char *s2)
+
+ \relates Q3CString
+
+ Returns true if \a s1 is greater than \a s2; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \> 0.
+*/
+
+/*!
+ \fn bool operator>(const char *s1, const Q3CString &s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 is greater than \a s2; otherwise returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \> 0.
+*/
+
+/*!
+ \fn bool operator>=(const Q3CString &s1, const char *s2)
+
+ \relates Q3CString
+
+ Returns true if \a s1 is greater than or equal to \a s2; otherwise
+ returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \>= 0.
+*/
+
+/*!
+ \fn bool operator>=(const char *s1, const Q3CString &s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns true if \a s1 is greater than or equal to \a s2; otherwise
+ returns false.
+
+ Equivalent to qstrcmp(\a s1, \a s2) \>= 0.
+*/
+
+/*!
+ \fn const Q3CString operator+(const Q3CString &s1, const Q3CString &s2)
+
+ \relates Q3CString
+
+ Returns a string which consists of the concatenation of \a s1 and
+ \a s2.
+*/
+
+/*!
+ \fn const Q3CString operator+(const Q3CString &s1, const char *s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns a string which consists of the concatenation of \a s1 and \a s2.
+*/
+
+/*!
+ \fn const Q3CString operator+(const char *s1, const Q3CString &s2)
+ \overload
+
+ \relates Q3CString
+
+ Returns a string which consists of the concatenation of \a s1 and \a s2.
+*/
+
+/*!
+ \fn const Q3CString operator+(const Q3CString &s, char c)
+ \overload
+
+ \relates Q3CString
+
+ Returns a string which consists of the concatenation of \a s and \a c.
+*/
+
+/*!
+ \fn const Q3CString operator+(char c, const Q3CString &s)
+ \overload
+
+ \relates Q3CString
+ Returns a string which consists of the concatenation of \a c and \a s.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3cstring.h b/src/qt3support/tools/q3cstring.h
new file mode 100644
index 0000000..da31763
--- /dev/null
+++ b/src/qt3support/tools/q3cstring.h
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3CSTRING_H
+#define Q3CSTRING_H
+
+#include <QtCore/qbytearray.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+/*****************************************************************************
+ QCString class
+ *****************************************************************************/
+
+class QRegExp;
+
+class Q_COMPAT_EXPORT Q3CString : public QByteArray
+{
+public:
+ Q3CString() {}
+ Q3CString(int size) : QByteArray(size, '\0') {}
+ Q3CString(const Q3CString &s) : QByteArray(s) {}
+ Q3CString(const QByteArray &ba) : QByteArray(ba) {}
+ Q3CString(const char *str) : QByteArray(str) {}
+ Q3CString(const char *str, uint maxlen) : QByteArray(str, qMin(qstrlen(str), maxlen - 1)) {}
+
+ Q3CString &operator=(const Q3CString &s) {
+ QByteArray::operator=(s); return *this;
+ }
+ Q3CString &operator=(const char *str) {
+ QByteArray::operator=(str); return *this;
+ }
+ Q3CString &operator=(const QByteArray &ba) {
+ QByteArray::operator=(ba); return *this;
+ }
+
+ Q3CString copy() const { return *this; }
+ Q3CString &sprintf(const char *format, ...);
+
+ Q3CString left(uint len) const { return QByteArray::left(len); }
+ Q3CString right(uint len) const { return QByteArray::right(len); }
+ Q3CString mid(uint index, uint len=0xffffffff) const { return QByteArray::mid(index, len); }
+
+ Q3CString leftJustify(uint width, char fill=' ', bool trunc=false)const;
+ Q3CString rightJustify(uint width, char fill=' ',bool trunc=false)const;
+
+ Q3CString lower() const { return QByteArray::toLower(); }
+ Q3CString upper() const { return QByteArray::toUpper(); }
+
+ Q3CString stripWhiteSpace() const { return QByteArray::trimmed(); }
+ Q3CString simplifyWhiteSpace() const { return QByteArray::simplified(); }
+
+ Q3CString &insert(uint index, const char *c) { QByteArray::insert(index, c); return *this; }
+ Q3CString &insert(uint index, char c) { QByteArray::insert(index, c); return *this; }
+ Q3CString &append(const char *c) { QByteArray::append(c); return *this; }
+ Q3CString &prepend(const char *c) { QByteArray::prepend(c); return *this; }
+ Q3CString &remove(uint index, uint len) { QByteArray::remove(index, len); return *this; }
+ Q3CString &replace(uint index, uint len, const char *c)
+ { QByteArray::replace(index, len, c); return *this; }
+ Q3CString &replace(char c, const Q3CString &after) { return replace(c, after.constData()); }
+ Q3CString &replace(char c, const char *after) { QByteArray::replace(c, after); return *this; }
+ Q3CString &replace(const Q3CString &b, const Q3CString &a)
+ { return replace(b.constData(), a.constData()); }
+ Q3CString &replace(const char *b, const char *a) { QByteArray::replace(b, a); return *this; }
+ Q3CString &replace(char b, char a) { QByteArray::replace(b, a); return *this; }
+
+ short toShort(bool *ok=0) const;
+ ushort toUShort(bool *ok=0) const;
+ int toInt(bool *ok=0) const;
+ uint toUInt(bool *ok=0) const;
+ long toLong(bool *ok=0) const;
+ ulong toULong(bool *ok=0) const;
+ float toFloat(bool *ok=0) const;
+ double toDouble(bool *ok=0) const;
+
+ Q3CString &setStr(const char *s) { *this = s; return *this; }
+ Q3CString &setNum(short);
+ Q3CString &setNum(ushort);
+ Q3CString &setNum(int);
+ Q3CString &setNum(uint);
+ Q3CString &setNum(long);
+ Q3CString &setNum(ulong);
+ Q3CString &setNum(float, char f='g', int prec=6);
+ Q3CString &setNum(double, char f='g', int prec=6);
+
+ bool setExpand(uint index, char c);
+
+};
+
+
+/*****************************************************************************
+ Q3CString stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+Q_COMPAT_EXPORT QDataStream &operator<<(QDataStream &d, const Q3CString &s);
+Q_COMPAT_EXPORT QDataStream &operator>>(QDataStream &d, Q3CString &s);
+#endif
+
+/*****************************************************************************
+ Q3CString inline functions
+ *****************************************************************************/
+
+inline Q3CString &Q3CString::setNum(short n)
+{ return setNum(long(n)); }
+
+inline Q3CString &Q3CString::setNum(ushort n)
+{ return setNum(ulong(n)); }
+
+inline Q3CString &Q3CString::setNum(int n)
+{ return setNum(long(n)); }
+
+inline Q3CString &Q3CString::setNum(uint n)
+{ return setNum(ulong(n)); }
+
+inline Q3CString &Q3CString::setNum(float n, char f, int prec)
+{ return setNum(double(n),f,prec); }
+
+/*****************************************************************************
+ Q3CString non-member operators
+ *****************************************************************************/
+
+Q_COMPAT_EXPORT_INLINE bool operator==(const Q3CString &s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) == 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator==(const Q3CString &s1, const char *s2)
+{ return qstrcmp(s1, s2) == 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator==(const char *s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) == 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator!=(const Q3CString &s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) != 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator!=(const Q3CString &s1, const char *s2)
+{ return qstrcmp(s1, s2) != 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator!=(const char *s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) != 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator<(const Q3CString &s1, const Q3CString& s2)
+{ return qstrcmp(s1, s2) < 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator<(const Q3CString &s1, const char *s2)
+{ return qstrcmp(s1, s2) < 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator<(const char *s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) < 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator<=(const Q3CString &s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) <= 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator<=(const Q3CString &s1, const char *s2)
+{ return qstrcmp(s1, s2) <= 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator<=(const char *s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) <= 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator>(const Q3CString &s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) > 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator>(const Q3CString &s1, const char *s2)
+{ return qstrcmp(s1, s2) > 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator>(const char *s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) > 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator>=(const Q3CString &s1, const Q3CString& s2)
+{ return qstrcmp(s1, s2) >= 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator>=(const Q3CString &s1, const char *s2)
+{ return qstrcmp(s1, s2) >= 0; }
+
+Q_COMPAT_EXPORT_INLINE bool operator>=(const char *s1, const Q3CString &s2)
+{ return qstrcmp(s1, s2) >= 0; }
+
+Q_COMPAT_EXPORT_INLINE const Q3CString operator+(const Q3CString &s1,
+ const Q3CString &s2)
+{
+ Q3CString tmp(s1);
+ tmp += s2;
+ return tmp;
+}
+Q_COMPAT_EXPORT_INLINE const Q3CString operator+(const Q3CString &s1,
+ const QByteArray &s2)
+{
+ QByteArray tmp(s1);
+ tmp += s2;
+ return tmp;
+}
+Q_COMPAT_EXPORT_INLINE const Q3CString operator+(const QByteArray &s1,
+ const Q3CString &s2)
+{
+ QByteArray tmp(s1);
+ tmp += s2;
+ return tmp;
+}
+
+Q_COMPAT_EXPORT_INLINE const Q3CString operator+(const Q3CString &s1, const char *s2)
+{
+ Q3CString tmp(s1);
+ tmp += s2;
+ return tmp;
+}
+
+Q_COMPAT_EXPORT_INLINE const Q3CString operator+(const char *s1, const Q3CString &s2)
+{
+ Q3CString tmp(s1);
+ tmp += s2;
+ return tmp;
+}
+
+Q_COMPAT_EXPORT_INLINE const Q3CString operator+(const Q3CString &s1, char c2)
+{
+ Q3CString tmp(s1);
+ tmp += c2;
+ return tmp;
+}
+
+Q_COMPAT_EXPORT_INLINE const Q3CString operator+(char c1, const Q3CString &s2)
+{
+ Q3CString tmp;
+ tmp += c1;
+ tmp += s2;
+ return tmp;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3CSTRING_H
diff --git a/src/qt3support/tools/q3deepcopy.cpp b/src/qt3support/tools/q3deepcopy.cpp
new file mode 100644
index 0000000..0d0ed0e
--- /dev/null
+++ b/src/qt3support/tools/q3deepcopy.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3deepcopy.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3DeepCopy
+ \brief The Q3DeepCopy class is a template class which ensures that
+ implicitly shared and explicitly shared classes reference unique
+ data.
+
+ \reentrant
+
+ \compat
+
+ Normally, shared copies reference the same data to optimize memory
+ use and for maximum speed. In the example below, \c s1, \c s2, \c
+ s3, \c s4 and \c s5 share data.
+
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3deepcopy.cpp 0
+
+ Q3DeepCopy can be used several ways to ensure that an object
+ references unique, unshared data. In the example below, \c s1, \c
+ s2 and \c s5 share data, while neither \c s3 nor \c s4 share data.
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3deepcopy.cpp 1
+
+ In the example below, \c s1, \c s2 and \c s5 share data, and \c s3
+ and \c s4 share data.
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3deepcopy.cpp 2
+
+ Q3DeepCopy can also provide safety in multithreaded applications
+ that use shared classes. In the example below, the variable \c
+ global_string is used safely since the data contained in \c
+ global_string is always a deep copy. This ensures that all threads
+ get a unique copy of the data, and that any assignments to \c
+ global_string will result in a deep copy.
+
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3deepcopy.cpp 3
+
+ \warning It is the application developer's responsibility to
+ protect the object shared across multiple threads.
+
+ The examples above use QString, which is an implicitly shared
+ class. The behavior of Q3DeepCopy is the same when using explicitly
+ shared classes like QByteArray.
+
+ Currently, Q3DeepCopy works with the following classes:
+ \list
+ \i QMemArray (including subclasses like QByteArray and QCString)
+ \i QMap
+ \i QString
+ \i QValueList (including subclasses like QStringList and QValueStack)
+ \i QValueVector
+ \endlist
+
+ \sa \link threads.html Thread Support in Qt \endlink
+*/
+
+/*!
+ \fn Q3DeepCopy::Q3DeepCopy()
+
+ Constructs an empty instance of type \e T.
+*/
+
+/*!
+ \fn Q3DeepCopy::Q3DeepCopy( const T &t )
+
+ Constructs a deep copy of \a t.
+*/
+
+/*!
+ \fn Q3DeepCopy<T>& Q3DeepCopy::operator=( const T &t )
+
+ Assigns a deep copy of \a t.
+*/
+
+/*!
+ \fn Q3DeepCopy::operator T ()
+
+ Returns a deep copy of the encapsulated data.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3deepcopy.h b/src/qt3support/tools/q3deepcopy.h
new file mode 100644
index 0000000..c50398d
--- /dev/null
+++ b/src/qt3support/tools/q3deepcopy.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DEEPCOPY_H
+#define Q3DEEPCOPY_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template <class T>
+class Q3DeepCopy
+{
+public:
+ inline Q3DeepCopy()
+ {
+ }
+
+ inline Q3DeepCopy(const T &t)
+ : deepcopy(t)
+ {
+ deepcopy.detach();
+ }
+
+ inline Q3DeepCopy<T> &operator=(const T &t)
+ {
+ deepcopy = t;
+ deepcopy.detach();
+ return *this;
+ }
+
+ inline operator T ()
+ {
+ T tmp = deepcopy;
+ tmp.detach();
+ return tmp;
+ }
+
+private:
+ T deepcopy;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DEEPCOPY_H
diff --git a/src/qt3support/tools/q3dict.h b/src/qt3support/tools/q3dict.h
new file mode 100644
index 0000000..750abbf
--- /dev/null
+++ b/src/qt3support/tools/q3dict.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DICT_H
+#define Q3DICT_H
+
+#include <Qt3Support/q3gdict.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3Dict
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GDict
+#endif
+{
+public:
+ Q3Dict(int size = 17, bool caseSensitive = true)
+ : Q3GDict(size, StringKey, caseSensitive, false) { }
+ Q3Dict(const Q3Dict<type> &d) : Q3GDict(d) { }
+ ~Q3Dict() { clear(); }
+ Q3Dict<type> &operator=(const Q3Dict<type> &d)
+ { return (Q3Dict<type>&)Q3GDict::operator=(d); }
+ uint count() const { return Q3GDict::count(); }
+ uint size() const { return Q3GDict::size(); }
+ bool isEmpty() const { return Q3GDict::count() == 0; }
+
+ void insert(const QString &k, const type *d)
+ { Q3GDict::look_string(k,(Item)d,1); }
+ void replace(const QString &k, const type *d)
+ { Q3GDict::look_string(k,(Item)d,2); }
+ bool remove(const QString &k) { return Q3GDict::remove_string(k); }
+ type *take(const QString &k) { return (type *)Q3GDict::take_string(k); }
+ type *find(const QString &k) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_string(k,0,0); }
+ type *operator[](const QString &k) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_string(k,0,0); }
+
+ void clear() { Q3GDict::clear(); }
+ void resize(uint n) { Q3GDict::resize(n); }
+ void statistics() const { Q3GDict::statistics(); }
+
+#ifdef qdoc
+protected:
+ virtual QDataStream& read(QDataStream &, Q3PtrCollection::Item &);
+ virtual QDataStream& write(QDataStream &, Q3PtrCollection::Item) const;
+#endif
+
+private:
+ void deleteItem(Item d);
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3Dict<void>::deleteItem(Item)
+{
+}
+#endif
+
+template<class type> inline void Q3Dict<type>::deleteItem(Q3PtrCollection::Item d)
+{
+ if (del_item) delete (type *)d;
+}
+
+template<class type>
+class Q3DictIterator : public Q3GDictIterator
+{
+public:
+ Q3DictIterator(const Q3Dict<type> &d) : Q3GDictIterator((Q3GDict &)d) { }
+ ~Q3DictIterator() {}
+ uint count() const { return dict->count(); }
+ bool isEmpty() const { return dict->count() == 0; }
+ type *toFirst() { return (type *)Q3GDictIterator::toFirst(); }
+ operator type *() const { return (type *)Q3GDictIterator::get(); }
+ type *operator*() { return (type *)Q3GDictIterator::get(); }
+ type *current() const { return (type *)Q3GDictIterator::get(); }
+ QString currentKey() const{ return Q3GDictIterator::getKeyString(); }
+ type *operator()() { return (type *)Q3GDictIterator::operator()(); }
+ type *operator++() { return (type *)Q3GDictIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GDictIterator::operator+=(j); }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DICT_H
diff --git a/src/qt3support/tools/q3dict.qdoc b/src/qt3support/tools/q3dict.qdoc
new file mode 100644
index 0000000..8fcbba4
--- /dev/null
+++ b/src/qt3support/tools/q3dict.qdoc
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3Dict
+ \brief The Q3Dict class is a template class that provides a
+ dictionary based on QString keys.
+ \compat
+
+ Q3Dict is implemented as a template class. Define a template
+ instance Q3Dict\<X\> to create a dictionary that operates on
+ pointers to X (X *).
+
+ A dictionary is a collection of key-value pairs. The key is a
+ QString used for insertion, removal and lookup. The value is a
+ pointer. Dictionaries provide very fast insertion and lookup.
+
+ If you want to use non-Unicode, plain 8-bit \c char* keys, use the
+ Q3AsciiDict template. A Q3Dict has the same performance as a
+ Q3AsciiDict. If you want to have a dictionary that maps QStrings to
+ QStrings use QMap.
+
+ The size() of the dictionary is very important. In order to get
+ good performance, you should use a suitably large prime number.
+ Suitable means equal to or larger than the maximum expected number
+ of dictionary items. Size is set in the constructor but may be
+ changed with resize().
+
+ Items are inserted with insert(); 0 pointers cannot be inserted.
+ Items are removed with remove(). All the items in a dictionary can
+ be removed with clear(). The number of items in the dictionary is
+ returned by count(). If the dictionary contains no items isEmpty()
+ returns TRUE. You can change an item's value with replace(). Items
+ are looked up with operator[](), or with find() which return a
+ pointer to the value or 0 if the given key does not exist. You can
+ take an item out of the dictionary with take().
+
+ Calling setAutoDelete(TRUE) for a dictionary tells it to delete
+ items that are removed. The default behavior is not to delete
+ items when they are removed.
+
+ When an item is inserted, the key is converted (hashed) to an
+ integer index into an internal hash array. This makes lookup very
+ fast.
+
+ Items with equal keys are allowed. When inserting two items with
+ the same key, only the last inserted item will be accessible (last
+ in, first out) until it is removed.
+
+ The Q3DictIterator class can traverse the dictionary, but only in
+ an arbitrary order. Multiple iterators may independently traverse
+ the same dictionary.
+
+ When inserting an item into a dictionary, only the pointer is
+ copied, not the item itself, i.e. a shallow copy is made. It is
+ possible to make the dictionary copy all of the item's data (a
+ deep copy) when an item is inserted. insert() calls the virtual
+ function Q3PtrCollection::newItem() for the item to be inserted.
+ Inherit a dictionary and reimplement newItem() if you want deep
+ copies.
+
+ When removing a dictionary item, the virtual function
+ Q3PtrCollection::deleteItem() is called. Q3Dict's default
+ implementation is to delete the item if auto-deletion is enabled.
+
+ \sa Q3DictIterator, Q3AsciiDict, Q3IntDict, Q3PtrDict
+*/
+
+
+/*!
+ \fn Q3Dict::Q3Dict( int size, bool caseSensitive )
+
+ Constructs a dictionary optimized for less than \a size entries.
+
+ We recommend setting \a size to a suitably large prime number
+ (e.g. a prime that's slightly larger than the expected number of
+ entries). This makes the hash distribution better which will lead
+ to faster lookup.
+
+ If \a caseSensitive is TRUE (the default), keys which differ only
+ by case are considered different.
+*/
+
+/*!
+ \fn Q3Dict::Q3Dict( const Q3Dict<type> &dict )
+
+ Constructs a copy of \a dict.
+
+ Each item in \a dict is inserted into this dictionary. Only the
+ pointers are copied (shallow copy).
+*/
+
+/*!
+ \fn Q3Dict::~Q3Dict()
+
+ Removes all items from the dictionary and destroys it. If
+ setAutoDelete() is TRUE, each value is deleted. All iterators that
+ access this dictionary will be reset.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn Q3Dict<type> &Q3Dict::operator=(const Q3Dict<type> &dict)
+
+ Assigns \a dict to this dictionary and returns a reference to this
+ dictionary.
+
+ This dictionary is first cleared, then each item in \a dict is
+ inserted into this dictionary. Only the pointers are copied
+ (shallow copy), unless newItem() has been reimplemented.
+*/
+
+/*!
+ \fn uint Q3Dict::count() const
+
+ Returns the number of items in the dictionary.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn uint Q3Dict::size() const
+
+ Returns the size of the internal hash array (as specified in the
+ constructor).
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3Dict::resize( uint newsize )
+
+ Changes the size of the hash table to \a newsize. The contents of
+ the dictionary are preserved, but all iterators on the dictionary
+ become invalid.
+*/
+
+/*!
+ \fn bool Q3Dict::isEmpty() const
+
+ Returns TRUE if the dictionary is empty, i.e. count() == 0;
+ otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3Dict::insert( const QString &key, const type *item )
+
+ Inserts the key \a key with value \a item into the dictionary.
+
+ Multiple items can have the same key, in which case only the last
+ item will be accessible using \l operator[]().
+
+ \a item may not be 0.
+
+ \sa replace()
+*/
+
+/*!
+ \fn void Q3Dict::replace( const QString &key, const type *item )
+
+ Replaces the value of the key, \a key with \a item.
+
+ If the item does not already exist, it will be inserted.
+
+ \a item may not be 0.
+
+ Equivalent to:
+ \snippet doc/src/snippets/code/doc_src_q3dict.cpp 0
+
+ If there are two or more items with equal keys, then the last item
+ that was inserted will be replaced.
+
+ \sa insert()
+*/
+
+/*!
+ \fn bool Q3Dict::remove( const QString &key )
+
+ Removes the item with \a key from the dictionary. Returns TRUE if
+ successful, i.e. if the item is in the dictionary; otherwise
+ returns FALSE.
+
+ If there are two or more items with equal keys, then the last item
+ that was inserted will be removed.
+
+ The removed item is deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that refer to the removed item will be
+ set to point to the next item in the dictionary's traversal order.
+
+ \sa take(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3Dict::take( const QString &key )
+
+ Takes the item with \a key out of the dictionary without deleting
+ it (even if \link Q3PtrCollection::setAutoDelete()
+ auto-deletion\endlink is enabled).
+
+ If there are two or more items with equal keys, then the last item
+ that was inserted will be taken.
+
+ Returns a pointer to the item taken out, or 0 if the key does not
+ exist in the dictionary.
+
+ All dictionary iterators that refer to the taken item will be set
+ to point to the next item in the dictionary traversal order.
+
+ \sa remove(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn void Q3Dict::clear()
+
+ Removes all items from the dictionary.
+
+ The removed items are deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that operate on the dictionary are reset.
+
+ \sa remove(), take(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3Dict::find( const QString &key ) const
+
+ Returns the item with key \a key, or 0 if the key does not exist
+ in the dictionary.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be found.
+
+ Equivalent to the [] operator.
+
+ \sa operator[]()
+*/
+
+/*!
+ \fn type *Q3Dict::operator[]( const QString &key ) const
+
+ Returns the item with key \a key, or 0 if the key does not
+ exist in the dictionary.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be found.
+
+ Equivalent to the find() function.
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3Dict::statistics() const
+
+ Debugging-only function that prints out the dictionary
+ distribution using qDebug().
+*/
+
+/*!
+ \fn QDataStream& Q3Dict::read( QDataStream &s, Q3PtrCollection::Item &item )
+
+ Reads a dictionary item from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3Dict::write( QDataStream &s, Q3PtrCollection::Item item ) const
+
+ Writes a dictionary \a item to the stream \a s and returns a
+ reference to the stream.
+
+ \sa read()
+*/
+
+/*!
+ \class Q3DictIterator
+ \brief The Q3DictIterator class provides an iterator for Q3Dict collections.
+ \compat
+
+ Q3DictIterator is implemented as a template class. Define a
+ template instance Q3DictIterator\<X\> to create a dictionary
+ iterator that operates on Q3Dict\<X\> (dictionary of X*).
+
+ The traversal order is arbitrary; when we speak of the "first",
+ "last" and "next" item we are talking in terms of this arbitrary
+ order.
+
+ Multiple iterators may independently traverse the same dictionary.
+ A Q3Dict knows about all the iterators that are operating on the
+ dictionary. When an item is removed from the dictionary, Q3Dict
+ updates all iterators that are referring to the removed item to
+ point to the next item in the (arbitrary) traversal order.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3dict.cpp 1
+ In the example we insert some pointers to line edits into a
+ dictionary, then iterate over the dictionary printing the strings
+ associated with the line edits.
+
+ \sa Q3Dict
+*/
+
+/*!
+ \fn Q3DictIterator::Q3DictIterator( const Q3Dict<type> &dict )
+
+ Constructs an iterator for \a dict. The current iterator item is
+ set to point to the first item in the dictionary, \a dict. First
+ in this context means first in the arbitrary traversal order.
+*/
+
+/*!
+ \fn Q3DictIterator::~Q3DictIterator()
+
+ Destroys the iterator.
+*/
+
+/*!
+ \fn uint Q3DictIterator::count() const
+
+ Returns the number of items in the dictionary over which the
+ iterator is operating.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3DictIterator::isEmpty() const
+
+ Returns TRUE if the dictionary is empty, i.e. count() == 0;
+ otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn type *Q3DictIterator::toFirst()
+
+ Resets the iterator, making the first item the first current item.
+ First in this context means first in the arbitrary traversal
+ order. Returns a pointer to this item.
+
+ If the dictionary is empty it sets the current item to 0 and
+ returns 0.
+*/
+
+/*!
+ \fn type *Q3DictIterator::operator*()
+ \internal
+*/
+
+/*!
+ \fn Q3DictIterator::operator type*() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+
+/*!
+ \fn type *Q3DictIterator::current() const
+
+ Returns a pointer to the current iterator item's value.
+*/
+
+/*!
+ \fn QString Q3DictIterator::currentKey() const
+
+ Returns the current iterator item's key.
+*/
+
+/*!
+ \fn type *Q3DictIterator::operator()()
+
+ Makes the next item current and returns the original current item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3DictIterator::operator++()
+
+ Prefix ++ makes the next item current and returns the new current
+ item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3DictIterator::operator+=( uint jump )
+ \internal
+ Sets the current item to the item \a jump positions after the current item,
+ and returns a pointer to that item.
+
+ If that item is beyond the last item or if the dictionary is empty,
+ it sets the current item to 0 and returns 0.
+*/
diff --git a/src/qt3support/tools/q3garray.cpp b/src/qt3support/tools/q3garray.cpp
new file mode 100644
index 0000000..b1f4864
--- /dev/null
+++ b/src/qt3support/tools/q3garray.cpp
@@ -0,0 +1,798 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#if defined(Q_CC_BOR)
+ // needed for qsort() because of a std namespace problem on Borland
+# include "qplatformdefs.h"
+#elif defined(Q_WS_WIN)
+ // needed for bsearch on some platforms
+# include "qt_windows.h"
+#endif
+
+#define Q3GARRAY_CPP
+#include "q3garray.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef QT_NO_THREAD
+# include "private/qmutexpool_p.h"
+#endif
+
+#if defined(Q_OS_WINCE)
+# include "qfunctions_wince.h"
+#endif
+QT_BEGIN_NAMESPACE
+
+/*
+ If USE_MALLOC isn't defined, we use new[] and delete[] to allocate
+ memory. The documentation for QMemArray<T>::assign() explicitly
+ mentions that the array is freed using free(), so don't mess around
+ with USE_MALLOC unless you know what you're doing.
+*/
+#define USE_MALLOC
+
+#undef NEW
+#undef DELETE
+
+#if defined(USE_MALLOC)
+#define NEW(type,size) ((type*)malloc(size*sizeof(type)))
+#define DELETE(array) (free((char*)array))
+#else
+#define NEW(type,size) (new type[size])
+#define DELETE(array) (delete[] array)
+#define DONT_USE_REALLOC // comment to use realloc()
+#endif
+
+/*!
+ \class Q3GArray
+ \reentrant
+ \brief The Q3GArray class is an internal class for implementing the QMemArray class.
+
+ \internal
+
+ Q3GArray is a strictly internal class that acts as base class for the
+ QMemArray template array.
+
+ It contains an array of bytes and has no notion of an array element.
+*/
+
+
+/*!
+ Constructs a null array.
+*/
+
+Q3GArray::Q3GArray()
+{
+ shd = newData();
+ Q_CHECK_PTR(shd);
+}
+
+/*!
+ Dummy constructor; does not allocate any data.
+
+ This constructor does not initialize any array data so subclasses
+ must do it. The intention is to make the code more efficient.
+*/
+
+Q3GArray::Q3GArray(int, int)
+ : shd(0)
+{
+}
+
+/*!
+ Constructs an array with room for \a size bytes.
+*/
+
+Q3GArray::Q3GArray(int size)
+{
+ if (size < 0) {
+#if defined(QT_CHECK_RANGE)
+ qWarning("Q3GArray: Cannot allocate array with negative length");
+#endif
+ size = 0;
+ }
+ shd = newData();
+ Q_CHECK_PTR(shd);
+ if (size == 0) // zero length
+ return;
+ shd->data = NEW(char,size);
+ Q_CHECK_PTR(shd->data);
+ shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+ shd->maxl =
+#endif
+ size;
+}
+
+/*!
+ Constructs a shallow copy of \a a.
+*/
+
+Q3GArray::Q3GArray(const Q3GArray &a)
+{
+ shd = a.shd;
+ shd->ref();
+}
+
+/*!
+ Dereferences the array data and deletes it if this was the last
+ reference.
+*/
+
+Q3GArray::~Q3GArray()
+{
+ if (shd && shd->deref()) { // delete when last reference
+ if (shd->data) // is lost
+ DELETE(shd->data);
+ deleteData(shd);
+ shd = 0;
+ }
+}
+
+
+/*!
+ \fn Q3GArray &Q3GArray::operator=(const Q3GArray &a)
+
+ Assigns a shallow copy of \a a to this array and returns a reference to
+ this array. Equivalent to assign().
+*/
+
+/*!
+ \fn void Q3GArray::detach()
+
+ Detaches this array from shared array data.
+*/
+
+/*!
+ \fn char *Q3GArray::data() const
+
+ Returns a pointer to the actual array data.
+*/
+
+/*!
+ \fn uint Q3GArray::nrefs() const
+
+ Returns the reference count.
+*/
+
+/*!
+ \fn uint Q3GArray::size() const
+
+ Returns the size of the array, in bytes.
+*/
+
+
+/*!
+ Returns true if this array is equal to \a a, otherwise false.
+ The comparison is bitwise, of course.
+*/
+
+bool Q3GArray::isEqual(const Q3GArray &a) const
+{
+ if (size() != a.size()) // different size
+ return false;
+ if (data() == a.data()) // has same data
+ return true;
+ return (size() ? memcmp(data(), a.data(), size()) : 0) == 0;
+}
+
+
+/*!
+ Resizes the array to \a newsize bytes. \a optim is either
+ MemOptim (the default) or SpeedOptim.
+*/
+bool Q3GArray::resize(uint newsize, Optimization optim)
+{
+#ifndef QT_Q3GARRAY_SPEED_OPTIM
+ Q_UNUSED(optim);
+#endif
+
+ if (newsize == shd->len
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+ && newsize == shd->maxl
+#endif
+ ) // nothing to do
+ return true;
+ if (newsize == 0) { // remove array
+ if (shd->data)
+ DELETE(shd->data);
+ shd->data = 0;
+ shd->len = 0;
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+ shd->maxl = 0;
+#endif
+ return true;
+ }
+
+ uint newmaxl = newsize;
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+ if (optim == SpeedOptim) {
+ if (newsize <= shd->maxl &&
+ (newsize * 4 > shd->maxl || shd->maxl <= 4)) {
+ shd->len = newsize;
+ return true;
+ }
+ newmaxl = 4;
+ while (newmaxl < newsize)
+ newmaxl *= 2;
+ // try to spare some memory
+ if (newmaxl >= 1024 * 1024 && newsize <= newmaxl - (newmaxl >> 2))
+ newmaxl -= newmaxl >> 2;
+ }
+ shd->maxl = newmaxl;
+#endif
+
+ if (shd->data) { // existing data
+#if defined(DONT_USE_REALLOC)
+ char *newdata = NEW(char,newsize); // manual realloc
+ memcpy(newdata, shd->data, QMIN(shd->len,newmaxl));
+ DELETE(shd->data);
+ shd->data = newdata;
+#else
+ shd->data = (char *)realloc(shd->data, newmaxl);
+#endif
+ } else {
+ shd->data = NEW(char,newmaxl);
+ }
+ if (!shd->data) // no memory
+ return false;
+ shd->len = newsize;
+ return true;
+}
+
+/*!\overload
+*/
+bool Q3GArray::resize(uint newsize)
+{
+ return resize(newsize, MemOptim);
+}
+
+
+/*!
+ Fills the array with the repeated occurrences of \a d, which is
+ \a sz bytes long.
+ If \a len is specified as different from -1, then the array will be
+ resized to \a len*sz before it is filled.
+
+ Returns true if successful, or false if the memory cannot be allocated
+ (only when \a len != -1).
+
+ \sa resize()
+*/
+
+bool Q3GArray::fill(const char *d, int len, uint sz)
+{
+ if (len < 0)
+ len = shd->len/sz; // default: use array length
+ else if (!resize(len*sz))
+ return false;
+ if (sz == 1) // 8 bit elements
+ memset(data(), *d, len);
+ else if (sz == 4) { // 32 bit elements
+ register Q_INT32 *x = (Q_INT32*)data();
+ Q_INT32 v = *((Q_INT32*)d);
+ while (len--)
+ *x++ = v;
+ } else if (sz == 2) { // 16 bit elements
+ register Q_INT16 *x = (Q_INT16*)data();
+ Q_INT16 v = *((Q_INT16*)d);
+ while (len--)
+ *x++ = v;
+ } else { // any other size elements
+ register char *x = data();
+ while (len--) { // more complicated
+ memcpy(x, d, sz);
+ x += sz;
+ }
+ }
+ return true;
+}
+
+/*!
+ \overload
+ Shallow copy. Dereference the current array and references the data
+ contained in \a a instead. Returns a reference to this array.
+ \sa operator=()
+*/
+
+Q3GArray &Q3GArray::assign(const Q3GArray &a)
+{
+ a.shd->ref(); // avoid 'a = a'
+ if (shd->deref()) { // delete when last reference
+ if (shd->data) // is lost
+ DELETE(shd->data);
+ deleteData(shd);
+ }
+ shd = a.shd;
+ return *this;
+}
+
+/*!
+ Shallow copy. Dereference the current array and references the
+ array data \a d, which contains \a len bytes.
+ Returns a reference to this array.
+
+ Do not delete \a d later, because Q3GArray takes care of that.
+*/
+
+Q3GArray &Q3GArray::assign(const char *d, uint len)
+{
+ if (shd->count > 1) { // disconnect this
+ shd->count--;
+ shd = newData();
+ Q_CHECK_PTR(shd);
+ } else {
+ if (shd->data)
+ DELETE(shd->data);
+ }
+ shd->data = (char *)d;
+ shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+ shd->maxl =
+#endif
+ len;
+ return *this;
+}
+
+/*!
+ Deep copy. Dereference the current array and obtains a copy of the data
+ contained in \a a instead. Returns a reference to this array.
+ \sa assign(), operator=()
+*/
+
+Q3GArray &Q3GArray::duplicate(const Q3GArray &a)
+{
+ if (a.shd == shd) { // a.duplicate(a) !
+ if (shd->count > 1) {
+ shd->count--;
+ register array_data *n = newData();
+ Q_CHECK_PTR(n);
+ if ((n->len=shd->len)) {
+ n->data = NEW(char,n->len);
+ Q_CHECK_PTR(n->data);
+ if (n->data)
+ memcpy(n->data, shd->data, n->len);
+ } else {
+ n->data = 0;
+ }
+ shd = n;
+ }
+ return *this;
+ }
+ char *oldptr = 0;
+ if (shd->count > 1) { // disconnect this
+ shd->count--;
+ shd = newData();
+ Q_CHECK_PTR(shd);
+ } else { // delete after copy was made
+ oldptr = shd->data;
+ }
+ if (a.shd->len) { // duplicate data
+ shd->data = NEW(char,a.shd->len);
+ Q_CHECK_PTR(shd->data);
+ if (shd->data)
+ memcpy(shd->data, a.shd->data, a.shd->len);
+ } else {
+ shd->data = 0;
+ }
+ shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+ shd->maxl =
+#endif
+ a.shd->len;
+ if (oldptr)
+ DELETE(oldptr);
+ return *this;
+}
+
+/*!
+ \overload
+ Deep copy. Dereferences the current array and obtains a copy of
+ \a len characters from array data \a d instead. Returns a reference
+ to this array.
+ \sa assign(), operator=()
+*/
+
+Q3GArray &Q3GArray::duplicate(const char *d, uint len)
+{
+ char *data;
+ if (d == 0 || len == 0) {
+ data = 0;
+ len = 0;
+ } else {
+ if (shd->count == 1 && shd->len == len) {
+ if (shd->data != d) // avoid self-assignment
+ memcpy(shd->data, d, len); // use same buffer
+ return *this;
+ }
+ data = NEW(char,len);
+ Q_CHECK_PTR(data);
+ memcpy(data, d, len);
+ }
+ if (shd->count > 1) { // detach
+ shd->count--;
+ shd = newData();
+ Q_CHECK_PTR(shd);
+ } else { // just a single reference
+ if (shd->data)
+ DELETE(shd->data);
+ }
+ shd->data = data;
+ shd->len =
+#ifdef QT_Q3GARRAY_SPEED_OPTIM
+ shd->maxl =
+#endif
+ len;
+ return *this;
+}
+
+/*!
+ Resizes this array to \a len bytes and copies the \a len bytes at
+ address \a d into it.
+
+ \warning This function disregards the reference count mechanism. If
+ other Q3GArrays reference the same data as this, all will be updated.
+*/
+
+void Q3GArray::store(const char *d, uint len)
+{ // store, but not deref
+ resize(len);
+ memcpy(shd->data, d, len);
+}
+
+
+/*!
+ \fn array_data *Q3GArray::sharedBlock() const
+
+ Returns a pointer to the shared array block.
+
+ \warning
+
+ Do not use this function. Using it is begging for trouble. We dare
+ not remove it, for fear of breaking code, but we \e strongly
+ discourage new use of it.
+*/
+
+/*!
+ \fn void Q3GArray::setSharedBlock(array_data *p)
+
+ Sets the shared array block to \a p.
+
+ \warning
+
+ Do not use this function. Using it is begging for trouble. We dare
+ not remove it, for fear of breaking code, but we \e strongly
+ discourage new use of it.
+*/
+
+
+/*!
+ Sets raw data and returns a reference to the array.
+
+ Dereferences the current array and sets the new array data to \a d and
+ the new array size to \a len. Do not attempt to resize or re-assign the
+ array data when raw data has been set.
+ Call resetRawData(d,len) to reset the array.
+
+ Setting raw data is useful because it sets QMemArray data without
+ allocating memory or copying data.
+
+ Example of intended use:
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3garray.cpp 0
+
+ Example of misuse (do not do this):
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3garray.cpp 1
+
+ \warning If you do not call resetRawData(), Q3GArray will attempt to
+ deallocate or reallocate the raw data, which might not be too good.
+ Be careful.
+*/
+
+Q3GArray &Q3GArray::setRawData(const char *d, uint len)
+{
+ duplicate(0, 0); // set null data
+ shd->data = (char *)d;
+ shd->len = len;
+ return *this;
+}
+
+/*!
+ Resets raw data.
+
+ The arguments must be the data, \a d, and length \a len, that were
+ passed to setRawData(). This is for consistency checking.
+*/
+
+void Q3GArray::resetRawData(const char *d, uint len)
+{
+ if (d != shd->data || len != shd->len) {
+#if defined(QT_CHECK_STATE)
+ qWarning("Q3GArray::resetRawData: Inconsistent arguments");
+#endif
+ return;
+ }
+ shd->data = 0;
+ shd->len = 0;
+}
+
+
+/*!
+ Finds the first occurrence of \a d in the array from position \a index,
+ where \a sz is the size of the \a d element.
+
+ Note that \a index is given in units of \a sz, not bytes.
+
+ This function only compares whole cells, not bytes.
+*/
+
+int Q3GArray::find(const char *d, uint index, uint sz) const
+{
+ index *= sz;
+ if (index >= shd->len) {
+#if defined(QT_CHECK_RANGE)
+ qWarning("Q3GArray::find: Index %d out of range", index/sz);
+#endif
+ return -1;
+ }
+ register uint i;
+ uint ii;
+ switch (sz) {
+ case 1: { // 8 bit elements
+ register char *x = data() + index;
+ char v = *d;
+ for (i=index; i<shd->len; i++) {
+ if (*x++ == v)
+ break;
+ }
+ ii = i;
+ }
+ break;
+ case 2: { // 16 bit elements
+ register Q_INT16 *x = (Q_INT16*)(data() + index);
+ Q_INT16 v = *((Q_INT16*)d);
+ for (i=index; i<shd->len; i+=2) {
+ if (*x++ == v)
+ break;
+ }
+ ii = i/2;
+ }
+ break;
+ case 4: { // 32 bit elements
+ register Q_INT32 *x = (Q_INT32*)(data() + index);
+ Q_INT32 v = *((Q_INT32*)d);
+ for (i=index; i<shd->len; i+=4) {
+ if (*x++ == v)
+ break;
+ }
+ ii = i/4;
+ }
+ break;
+ default: { // any size elements
+ for (i=index; i<shd->len; i+=sz) {
+ if (memcmp(d, &shd->data[i], sz) == 0)
+ break;
+ }
+ ii = i/sz;
+ }
+ break;
+ }
+ return i<shd->len ? (int)ii : -1;
+}
+
+/*!
+ Returns the number of occurrences of \a d in the array, where \a sz is
+ the size of the \a d element.
+
+ This function only compares whole cells, not bytes.
+*/
+
+int Q3GArray::contains(const char *d, uint sz) const
+{
+ register uint i = shd->len;
+ int count = 0;
+ switch (sz) {
+ case 1: { // 8 bit elements
+ register char *x = data();
+ char v = *d;
+ while (i--) {
+ if (*x++ == v)
+ count++;
+ }
+ }
+ break;
+ case 2: { // 16 bit elements
+ register Q_INT16 *x = (Q_INT16*)data();
+ Q_INT16 v = *((Q_INT16*)d);
+ i /= 2;
+ while (i--) {
+ if (*x++ == v)
+ count++;
+ }
+ }
+ break;
+ case 4: { // 32 bit elements
+ register Q_INT32 *x = (Q_INT32*)data();
+ Q_INT32 v = *((Q_INT32*)d);
+ i /= 4;
+ while (i--) {
+ if (*x++ == v)
+ count++;
+ }
+ }
+ break;
+ default: { // any size elements
+ for (i=0; i<shd->len; i+=sz) {
+ if (memcmp(d, &shd->data[i], sz) == 0)
+ count++;
+ }
+ }
+ break;
+ }
+ return count;
+}
+
+static int cmp_item_size = 0;
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int __cdecl cmp_arr(const void *n1, const void *n2)
+#else
+static int cmp_arr(const void *n1, const void *n2)
+#endif
+{
+ return (n1 && n2) ? memcmp(n1, n2, cmp_item_size)
+ : (n1 ? 1 : (n2 ? -1 : 0));
+ // ### Qt 3.0: Add a virtual compareItems() method and call that instead
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+/*!
+ Sorts the first \a sz items of the array.
+*/
+
+void Q3GArray::sort(uint sz)
+{
+ int numItems = size() / sz;
+ if (numItems < 2)
+ return;
+
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&cmp_item_size));
+#endif
+
+ cmp_item_size = sz;
+ qsort(shd->data, numItems, sz, cmp_arr);
+}
+
+/*!
+ Binary search; assumes that \a d is a sorted array of size \a sz.
+*/
+
+int Q3GArray::bsearch(const char *d, uint sz) const
+{
+ int numItems = size() / sz;
+ if (!numItems)
+ return -1;
+
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&cmp_item_size));
+#endif
+
+ cmp_item_size = sz;
+ char* r = (char*)::bsearch(d, shd->data, numItems, sz, cmp_arr);
+ if (!r)
+ return -1;
+ while((r >= shd->data + sz) && (cmp_arr(r - sz, d) == 0))
+ r -= sz; // search to first of equal elements; bsearch is undef
+ return (int)((r - shd->data) / sz);
+}
+
+
+/*!
+ \fn char *Q3GArray::at(uint index) const
+
+ Returns a pointer to the byte at offset \a index in the array.
+*/
+
+/*!
+ Expand the array if necessary, and copies (the first part of) its
+ contents from the \a index * \a sz bytes at \a d.
+
+ Returns true if the operation succeeds, false if it runs out of
+ memory.
+
+ \warning This function disregards the reference count mechanism. If
+ other Q3GArrays reference the same data as this, all will be changed.
+*/
+
+bool Q3GArray::setExpand(uint index, const char *d, uint sz)
+{
+ index *= sz;
+ if (index >= shd->len) {
+ if (!resize(index+sz)) // no memory
+ return false;
+ }
+ memcpy(data() + index, d, sz);
+ return true;
+}
+
+
+/*!
+ Prints a warning message if at() or [] is given a bad index.
+*/
+
+void Q3GArray::msg_index(uint index)
+{
+#if defined(QT_CHECK_RANGE)
+ qWarning("Q3GArray::at: Absolute index %d out of range", index);
+#else
+ Q_UNUSED(index)
+#endif
+}
+
+
+/*!
+ Returns a new shared array block.
+*/
+
+Q3GArray::array_data * Q3GArray::newData()
+{
+ return new array_data;
+}
+
+
+/*!
+ Deletes the shared array block \a p.
+*/
+
+void Q3GArray::deleteData(array_data *p)
+{
+ delete p;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3garray.h b/src/qt3support/tools/q3garray.h
new file mode 100644
index 0000000..2dd3124
--- /dev/null
+++ b/src/qt3support/tools/q3garray.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GARRAY_H
+#define Q3GARRAY_H
+
+#include <Qt3Support/q3shared.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3GArray // generic array
+{
+ friend class QBuffer;
+public:
+ // do not use this, even though this is public
+ struct array_data : public Q3Shared { // shared array
+ array_data():data(0),len(0)
+#ifdef QT_QGARRAY_SPEED_OPTIM
+ ,maxl(0)
+#endif
+ {}
+ char *data; // actual array data
+ uint len;
+#ifdef QT_QGARRAY_SPEED_OPTIM
+ uint maxl;
+#endif
+ };
+ Q3GArray();
+ enum Optimization { MemOptim, SpeedOptim };
+protected:
+ Q3GArray(int, int); // dummy; does not alloc
+ Q3GArray(int size); // allocate 'size' bytes
+ Q3GArray(const Q3GArray &a); // shallow copy
+ virtual ~Q3GArray();
+
+ Q3GArray &operator=(const Q3GArray &a) { return assign(a); }
+
+ virtual void detach() { duplicate(*this); }
+
+ // ### Qt 4.0: maybe provide two versions of data(), at(), etc.
+ char *data() const { return shd->data; }
+ uint nrefs() const { return shd->count; }
+ uint size() const { return shd->len; }
+ bool isEqual(const Q3GArray &a) const;
+
+ bool resize(uint newsize, Optimization optim);
+ bool resize(uint newsize);
+
+ bool fill(const char *d, int len, uint sz);
+
+ Q3GArray &assign(const Q3GArray &a);
+ Q3GArray &assign(const char *d, uint len);
+ Q3GArray &duplicate(const Q3GArray &a);
+ Q3GArray &duplicate(const char *d, uint len);
+ void store(const char *d, uint len);
+
+ array_data *sharedBlock() const { return shd; }
+ void setSharedBlock(array_data *p) { shd=(array_data*)p; }
+
+ Q3GArray &setRawData(const char *d, uint len);
+ void resetRawData(const char *d, uint len);
+
+ int find(const char *d, uint index, uint sz) const;
+ int contains(const char *d, uint sz) const;
+
+ void sort(uint sz);
+ int bsearch(const char *d, uint sz) const;
+
+ char *at(uint index) const;
+
+ bool setExpand(uint index, const char *d, uint sz);
+
+protected:
+ virtual array_data *newData();
+ virtual void deleteData(array_data *p);
+
+private:
+ static void msg_index(uint);
+ array_data *shd;
+};
+
+
+inline char *Q3GArray::at(uint index) const
+{
+#if defined(QT_CHECK_RANGE)
+ if (index >= size()) {
+ msg_index(index);
+ index = 0;
+ }
+#endif
+ return &shd->data[index];
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GARRAY_H
diff --git a/src/qt3support/tools/q3gcache.cpp b/src/qt3support/tools/q3gcache.cpp
new file mode 100644
index 0000000..c46faed
--- /dev/null
+++ b/src/qt3support/tools/q3gcache.cpp
@@ -0,0 +1,867 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3gcache.h"
+#include "q3ptrlist.h"
+#include "q3dict.h"
+#include "qstring.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3GCache
+ \reentrant
+ \brief The Q3GCache class is an internal class for implementing Q3Cache
+ template classes.
+
+ \internal
+
+ Q3GCache is a strictly internal class that acts as a base class for the
+ \link collection.html collection classes\endlink Q3Cache and QIntCache.
+*/
+
+
+/*****************************************************************************
+ Q3GCacheItem class (internal cache item)
+ *****************************************************************************/
+
+struct Q3CacheItem
+{
+ Q3CacheItem(void *k, Q3PtrCollection::Item d, int c, short p)
+ : priority(p), skipPriority(p), cost(c), key(k), data(d), node(0) {}
+ short priority;
+ short skipPriority;
+ int cost;
+ void *key;
+ Q3PtrCollection::Item data;
+ Q3LNode *node;
+};
+
+
+/*****************************************************************************
+ Q3CList class (internal list of cache items)
+ *****************************************************************************/
+
+class Q3CList : private Q3PtrList<Q3CacheItem>
+{
+friend class Q3GCacheIterator;
+friend class Q3CListIt;
+public:
+ Q3CList() {}
+ ~Q3CList();
+
+ void insert(Q3CacheItem *); // insert according to priority
+ void insert(int, Q3CacheItem *);
+ void take(Q3CacheItem *);
+ void reference(Q3CacheItem *);
+
+ void setAutoDelete(bool del) { Q3PtrCollection::setAutoDelete(del); }
+
+ bool removeFirst() { return Q3PtrList<Q3CacheItem>::removeFirst(); }
+ bool removeLast() { return Q3PtrList<Q3CacheItem>::removeLast(); }
+
+ Q3CacheItem *first() { return Q3PtrList<Q3CacheItem>::first(); }
+ Q3CacheItem *last() { return Q3PtrList<Q3CacheItem>::last(); }
+ Q3CacheItem *prev() { return Q3PtrList<Q3CacheItem>::prev(); }
+ Q3CacheItem *next() { return Q3PtrList<Q3CacheItem>::next(); }
+
+#if defined(QT_DEBUG)
+ int inserts; // variables for statistics
+ int insertCosts;
+ int insertMisses;
+ int finds;
+ int hits;
+ int hitCosts;
+ int dumps;
+ int dumpCosts;
+#endif
+};
+
+
+Q3CList::~Q3CList()
+{
+#if defined(QT_DEBUG)
+ Q_ASSERT(count() == 0);
+#endif
+}
+
+
+void Q3CList::insert(Q3CacheItem *ci)
+{
+ Q3CacheItem *item = first();
+ while(item && item->skipPriority > ci->priority) {
+ item->skipPriority--;
+ item = next();
+ }
+ if (item)
+ Q3PtrList<Q3CacheItem>::insert(at(), ci);
+ else
+ append(ci);
+#if defined(QT_DEBUG)
+ Q_ASSERT(ci->node == 0);
+#endif
+ ci->node = currentNode();
+}
+
+inline void Q3CList::insert(int i, Q3CacheItem *ci)
+{
+ Q3PtrList<Q3CacheItem>::insert(i, ci);
+#if defined(QT_DEBUG)
+ Q_ASSERT(ci->node == 0);
+#endif
+ ci->node = currentNode();
+}
+
+
+void Q3CList::take(Q3CacheItem *ci)
+{
+ if (ci) {
+#if defined(QT_DEBUG)
+ Q_ASSERT(ci->node != 0);
+#endif
+ takeNode(ci->node);
+ ci->node = 0;
+ }
+}
+
+
+inline void Q3CList::reference(Q3CacheItem *ci)
+{
+#if defined(QT_DEBUG)
+ Q_ASSERT(ci != 0 && ci->node != 0);
+#endif
+ ci->skipPriority = ci->priority;
+ relinkNode(ci->node); // relink as first item
+}
+
+
+class Q3CListIt: public Q3PtrListIterator<Q3CacheItem>
+{
+public:
+ Q3CListIt(const Q3CList *p): Q3PtrListIterator<Q3CacheItem>(*p) {}
+ Q3CListIt(const Q3CListIt *p): Q3PtrListIterator<Q3CacheItem>(*p) {}
+};
+
+
+/*****************************************************************************
+ Q3CDict class (internal dictionary of cache items)
+ *****************************************************************************/
+
+//
+// Since we need to decide if the dictionary should use an int or const
+// char * key (the "bool trivial" argument in the constructor below)
+// we cannot use the macro/template dict, but inherit directly from Q3GDict.
+//
+
+class Q3CDict : public Q3GDict
+{
+public:
+ Q3CDict(uint size, uint kt, bool caseSensitive, bool copyKeys)
+ : Q3GDict(size, (KeyType)kt, caseSensitive, copyKeys) {}
+ ~Q3CDict();
+
+ void clear() { Q3GDict::clear(); }
+
+ Q3CacheItem *find_string(const QString &key) const
+ { return (Q3CacheItem*)((Q3CDict*)this)->look_string(key, 0, 0); }
+ Q3CacheItem *find_ascii(const char *key) const
+ { return (Q3CacheItem*)((Q3CDict*)this)->look_ascii(key, 0, 0); }
+ Q3CacheItem *find_int(long key) const
+ { return (Q3CacheItem*)((Q3CDict*)this)->look_int(key, 0, 0); }
+
+ Q3CacheItem *take_string(const QString &key)
+ { return (Q3CacheItem*)Q3GDict::take_string(key); }
+ Q3CacheItem *take_ascii(const char *key)
+ { return (Q3CacheItem*)Q3GDict::take_ascii(key); }
+ Q3CacheItem *take_int(long key)
+ { return (Q3CacheItem*)Q3GDict::take_int(key); }
+
+ bool insert_string(const QString &key, const Q3CacheItem *ci)
+ { return Q3GDict::look_string(key,(Item)ci,1)!=0;}
+ bool insert_ascii(const char *key, const Q3CacheItem *ci)
+ { return Q3GDict::look_ascii(key,(Item)ci,1)!=0;}
+ bool insert_int(long key, const Q3CacheItem *ci)
+ { return Q3GDict::look_int(key,(Item)ci,1)!=0;}
+
+ bool remove_string(Q3CacheItem *item)
+ { return Q3GDict::remove_string(*((QString*)(item->key)),item); }
+ bool remove_ascii(Q3CacheItem *item)
+ { return Q3GDict::remove_ascii((const char *)item->key,item); }
+ bool remove_int(Q3CacheItem *item)
+ { return Q3GDict::remove_int((quintptr)item->key,item);}
+
+ void statistics() { Q3GDict::statistics(); }
+
+private:
+ void deleteItem(void *item)
+ { if (del_item) { Q3CacheItem *d = (Q3CacheItem*)item; delete d; } }
+};
+
+inline Q3CDict::~Q3CDict()
+{
+ clear();
+}
+
+/*****************************************************************************
+ Q3GDict member functions
+ *****************************************************************************/
+
+/*!
+ Constructs a cache.
+ The maximum cost of the cache is given by \a maxCost and the size by \a
+ size. The key type is \a kt which may be \c StringKey, \c AsciiKey,
+ \c IntKey or \c PtrKey. The case-sensitivity of lookups is set with
+ \a caseSensitive. Keys are copied if \a copyKeys is true.
+*/
+
+Q3GCache::Q3GCache(int maxCost, uint size, KeyType kt, bool caseSensitive,
+ bool copyKeys)
+{
+ keytype = kt;
+ lruList = new Q3CList;
+ Q_CHECK_PTR(lruList);
+ lruList->setAutoDelete(true);
+ copyk = ((keytype == AsciiKey) && copyKeys);
+ dict = new Q3CDict(size, kt, caseSensitive, false);
+ Q_CHECK_PTR(dict);
+ mCost = maxCost;
+ tCost = 0;
+#if defined(QT_DEBUG)
+ lruList->inserts = 0;
+ lruList->insertCosts = 0;
+ lruList->insertMisses = 0;
+ lruList->finds = 0;
+ lruList->hits = 0;
+ lruList->hitCosts = 0;
+ lruList->dumps = 0;
+ lruList->dumpCosts = 0;
+#endif
+}
+
+/*!
+ Cannot copy a cache.
+*/
+
+Q3GCache::Q3GCache(const Q3GCache &)
+ : Q3PtrCollection()
+{
+#if defined(QT_CHECK_NULL)
+ qFatal("Q3GCache::Q3GCache(Q3GCache &): Cannot copy a cache");
+#endif
+}
+
+/*!
+ Removes all items from the cache and destroys it.
+*/
+
+Q3GCache::~Q3GCache()
+{
+ clear();
+ delete dict;
+ delete lruList;
+}
+
+/*!
+ Cannot assign a cache.
+*/
+
+Q3GCache &Q3GCache::operator=(const Q3GCache &)
+{
+#if defined(QT_CHECK_NULL)
+ qFatal("Q3GCache::operator=: Cannot copy a cache");
+#endif
+ return *this;
+}
+
+
+/*!
+ Returns the number of items in the cache.
+*/
+
+uint Q3GCache::count() const
+{
+ return dict->count();
+}
+
+/*!
+ Returns the size of the hash array.
+*/
+
+uint Q3GCache::size() const
+{
+ return dict->size();
+}
+
+/*!
+ \fn int Q3GCache::maxCost() const
+
+ Returns the maximum cache cost.
+*/
+
+/*!
+ \fn int Q3GCache::totalCost() const
+
+ Returns the total cache cost.
+*/
+
+/*!
+ Sets the maximum cache cost to \a maxCost.
+*/
+
+void Q3GCache::setMaxCost(int maxCost)
+{
+ if (maxCost < tCost) {
+ if (!makeRoomFor(tCost - maxCost)) // remove excess cost
+ return;
+ }
+ mCost = maxCost;
+}
+
+
+/*!
+ Inserts an item with data \a data into the cache using key \a key.
+ The item has cost \a cost and priority \a priority.
+
+ \warning If this function returns false, you must delete \a data
+ yourself. Additionally, be very careful about using \a data after
+ calling this function, as any other insertions into the cache, from
+ anywhere in the application, or within Qt itself, could cause the
+ data to be discarded from the cache, and the pointer to become
+ invalid.
+*/
+
+bool Q3GCache::insert_string(const QString &key, Q3PtrCollection::Item data,
+ int cost, int priority)
+{
+ if (tCost + cost > mCost) {
+ if (!makeRoomFor(tCost + cost - mCost, priority)) {
+#if defined(QT_DEBUG)
+ lruList->insertMisses++;
+#endif
+ return false;
+ }
+ }
+#if defined(QT_DEBUG)
+ Q_ASSERT(keytype == StringKey);
+ lruList->inserts++;
+ lruList->insertCosts += cost;
+#endif
+ if (priority < -32768)
+ priority = -32768;
+ else if (priority > 32767)
+ priority = 32677;
+ Q3CacheItem *ci = new Q3CacheItem(new QString(key), newItem(data),
+ cost, (short)priority);
+ Q_CHECK_PTR(ci);
+ lruList->insert(0, ci);
+ dict->insert_string(key, ci);
+ tCost += cost;
+ return true;
+}
+
+bool Q3GCache::insert_other(const char *key, Q3PtrCollection::Item data,
+ int cost, int priority)
+{
+ if (tCost + cost > mCost) {
+ if (!makeRoomFor(tCost + cost - mCost, priority)) {
+#if defined(QT_DEBUG)
+ lruList->insertMisses++;
+#endif
+ return false;
+ }
+ }
+#if defined(QT_DEBUG)
+ Q_ASSERT(keytype != StringKey);
+ lruList->inserts++;
+ lruList->insertCosts += cost;
+#endif
+ if (keytype == AsciiKey && copyk)
+ key = qstrdup(key);
+ if (priority < -32768)
+ priority = -32768;
+ else if (priority > 32767)
+ priority = 32677;
+ Q3CacheItem *ci = new Q3CacheItem((void*)key, newItem(data), cost,
+ (short)priority);
+ Q_CHECK_PTR(ci);
+ lruList->insert(0, ci);
+ if (keytype == AsciiKey)
+ dict->insert_ascii(key, ci);
+ else
+ dict->insert_int((quintptr)key, ci);
+ tCost += cost;
+ return true;
+}
+
+
+/*!
+ Removes the item with key \a key from the cache. Returns true if the
+ item was removed; otherwise returns false.
+*/
+
+bool Q3GCache::remove_string(const QString &key)
+{
+ Item d = take_string(key);
+ if (d)
+ deleteItem(d);
+ return d != 0;
+}
+
+bool Q3GCache::remove_other(const char *key)
+{
+ Item d = take_other(key);
+ if (d)
+ deleteItem(d);
+ return d != 0;
+}
+
+
+/*!
+ Takes the item with key \a key out of the cache. The item is not
+ deleted. If no item has this \a key 0 is returned.
+*/
+
+Q3PtrCollection::Item Q3GCache::take_string(const QString &key)
+{
+ Q3CacheItem *ci = dict->take_string(key); // take from dict
+ Item d;
+ if (ci) {
+ d = ci->data;
+ tCost -= ci->cost;
+ lruList->take(ci); // take from list
+ delete (QString*)ci->key;
+ delete ci;
+ } else {
+ d = 0;
+ }
+ return d;
+}
+
+/*!
+ Takes the item with key \a key out of the cache. The item is not
+ deleted. If no item has this \a key 0 is returned.
+*/
+
+Q3PtrCollection::Item Q3GCache::take_other(const char *key)
+{
+ Q3CacheItem *ci;
+ if (keytype == AsciiKey)
+ ci = dict->take_ascii(key);
+ else
+ ci = dict->take_int((quintptr)key);
+ Item d;
+ if (ci) {
+ d = ci->data;
+ tCost -= ci->cost;
+ lruList->take(ci); // take from list
+ if (copyk)
+ delete [] (char *)ci->key;
+ delete ci;
+ } else {
+ d = 0;
+ }
+ return d;
+}
+
+
+/*!
+ Clears the cache.
+*/
+
+void Q3GCache::clear()
+{
+ Q3CacheItem *ci;
+ while ((ci = lruList->first())) {
+ switch (keytype) {
+ case StringKey:
+ dict->remove_string(ci);
+ delete (QString*)ci->key;
+ break;
+ case AsciiKey:
+ dict->remove_ascii(ci);
+ if (copyk)
+ delete [] (char*)ci->key;
+ break;
+ case IntKey:
+ dict->remove_int(ci);
+ break;
+ case PtrKey: // unused
+ break;
+ }
+ deleteItem(ci->data); // delete data
+ lruList->removeFirst(); // remove from list
+ }
+ tCost = 0;
+}
+
+
+/*!
+ Finds an item for \a key in the cache and adds a reference if \a ref is true.
+*/
+
+Q3PtrCollection::Item Q3GCache::find_string(const QString &key, bool ref) const
+{
+ Q3CacheItem *ci = dict->find_string(key);
+#if defined(QT_DEBUG)
+ lruList->finds++;
+#endif
+ if (ci) {
+#if defined(QT_DEBUG)
+ lruList->hits++;
+ lruList->hitCosts += ci->cost;
+#endif
+ if (ref)
+ lruList->reference(ci);
+ return ci->data;
+ }
+ return 0;
+}
+
+
+/*!
+ Finds an item for \a key in the cache and adds a reference if \a ref is true.
+*/
+
+Q3PtrCollection::Item Q3GCache::find_other(const char *key, bool ref) const
+{
+ Q3CacheItem *ci = keytype == AsciiKey ? dict->find_ascii(key)
+ : dict->find_int((quintptr)key);
+#if defined(QT_DEBUG)
+ lruList->finds++;
+#endif
+ if (ci) {
+#if defined(QT_DEBUG)
+ lruList->hits++;
+ lruList->hitCosts += ci->cost;
+#endif
+ if (ref)
+ lruList->reference(ci);
+ return ci->data;
+ }
+ return 0;
+}
+
+
+/*!
+ Allocates cache space for one or more items.
+*/
+
+bool Q3GCache::makeRoomFor(int cost, int priority)
+{
+ if (cost > mCost) // cannot make room for more
+ return false; // than maximum cost
+ if (priority == -1)
+ priority = 32767;
+ register Q3CacheItem *ci = lruList->last();
+ int cntCost = 0;
+ int dumps = 0; // number of items to dump
+ while (cntCost < cost && ci && ci->skipPriority <= priority) {
+ cntCost += ci->cost;
+ ci = lruList->prev();
+ dumps++;
+ }
+ if (cntCost < cost) // can enough cost be dumped?
+ return false; // no
+#if defined(QT_DEBUG)
+ Q_ASSERT(dumps > 0);
+#endif
+ while (dumps--) {
+ ci = lruList->last();
+#if defined(QT_DEBUG)
+ lruList->dumps++;
+ lruList->dumpCosts += ci->cost;
+#endif
+ switch (keytype) {
+ case StringKey:
+ dict->remove_string(ci);
+ delete (QString*)ci->key;
+ break;
+ case AsciiKey:
+ dict->remove_ascii(ci);
+ if (copyk)
+ delete [] (char *)ci->key;
+ break;
+ case IntKey:
+ dict->remove_int(ci);
+ break;
+ case PtrKey: // unused
+ break;
+ }
+ deleteItem(ci->data); // delete data
+ lruList->removeLast(); // remove from list
+ }
+ tCost -= cntCost;
+ return true;
+}
+
+
+/*!
+ Outputs debug statistics.
+*/
+
+void Q3GCache::statistics() const
+{
+#if defined(QT_DEBUG)
+ QString line;
+ line.fill(QLatin1Char('*'), 80);
+ qDebug("%s", line.ascii());
+ qDebug("CACHE STATISTICS:");
+ qDebug("cache contains %d item%s, with a total cost of %d",
+ count(), count() != 1 ? "s" : "", tCost);
+ qDebug("maximum cost is %d, cache is %d%% full.",
+ mCost, (200*tCost + mCost) / (mCost*2));
+ qDebug("find() has been called %d time%s",
+ lruList->finds, lruList->finds != 1 ? "s" : "");
+ qDebug("%d of these were hits, items found had a total cost of %d.",
+ lruList->hits,lruList->hitCosts);
+ qDebug("%d item%s %s been inserted with a total cost of %d.",
+ lruList->inserts,lruList->inserts != 1 ? "s" : "",
+ lruList->inserts != 1 ? "have" : "has", lruList->insertCosts);
+ qDebug("%d item%s %s too large or had too low priority to be inserted.",
+ lruList->insertMisses, lruList->insertMisses != 1 ? "s" : "",
+ lruList->insertMisses != 1 ? "were" : "was");
+ qDebug("%d item%s %s been thrown away with a total cost of %d.",
+ lruList->dumps, lruList->dumps != 1 ? "s" : "",
+ lruList->dumps != 1 ? "have" : "has", lruList->dumpCosts);
+ qDebug("Statistics from internal dictionary class:");
+ dict->statistics();
+ qDebug("%s", line.ascii());
+#endif
+}
+
+
+/*****************************************************************************
+ Q3GCacheIterator member functions
+ *****************************************************************************/
+
+/*!
+ \class Q3GCacheIterator
+ \reentrant
+ \brief The Q3GCacheIterator class is an internal class for implementing Q3CacheIterator and
+ QIntCacheIterator.
+
+ \internal
+
+ Q3GCacheIterator is a strictly internal class that does the heavy work for
+ Q3CacheIterator and QIntCacheIterator.
+*/
+
+/*!
+ Constructs an iterator that operates on the cache \a c.
+*/
+
+Q3GCacheIterator::Q3GCacheIterator(const Q3GCache &c)
+{
+ it = new Q3CListIt(c.lruList);
+#if defined(QT_DEBUG)
+ Q_ASSERT(it != 0);
+#endif
+}
+
+/*!
+ Constructs an iterator that operates on the same cache as \a ci.
+*/
+
+Q3GCacheIterator::Q3GCacheIterator(const Q3GCacheIterator &ci)
+{
+ it = new Q3CListIt(ci.it);
+#if defined(QT_DEBUG)
+ Q_ASSERT(it != 0);
+#endif
+}
+
+/*!
+ Destroys the iterator.
+*/
+
+Q3GCacheIterator::~Q3GCacheIterator()
+{
+ delete it;
+}
+
+/*!
+ Assigns the iterator \a ci to this cache iterator.
+*/
+
+Q3GCacheIterator &Q3GCacheIterator::operator=(const Q3GCacheIterator &ci)
+{
+ *it = *ci.it;
+ return *this;
+}
+
+/*!
+ Returns the number of items in the cache.
+*/
+
+uint Q3GCacheIterator::count() const
+{
+ return it->count();
+}
+
+/*!
+ Returns true if the iterator points to the first item.
+*/
+
+bool Q3GCacheIterator::atFirst() const
+{
+ return it->atFirst();
+}
+
+/*!
+ Returns true if the iterator points to the last item.
+*/
+
+bool Q3GCacheIterator::atLast() const
+{
+ return it->atLast();
+}
+
+/*!
+ Sets the list iterator to point to the first item in the cache.
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::toFirst()
+{
+ Q3CacheItem *item = it->toFirst();
+ return item ? item->data : 0;
+}
+
+/*!
+ Sets the list iterator to point to the last item in the cache.
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::toLast()
+{
+ Q3CacheItem *item = it->toLast();
+ return item ? item->data : 0;
+}
+
+/*!
+ Returns the current item.
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::get() const
+{
+ Q3CacheItem *item = it->current();
+ return item ? item->data : 0;
+}
+
+/*!
+ Returns the key of the current item.
+*/
+
+QString Q3GCacheIterator::getKeyString() const
+{
+ Q3CacheItem *item = it->current();
+ return item ? *((QString*)item->key) : QString();
+}
+
+/*!
+ Returns the key of the current item, as a \0-terminated C string.
+*/
+
+const char *Q3GCacheIterator::getKeyAscii() const
+{
+ Q3CacheItem *item = it->current();
+ return item ? (const char *)item->key : 0;
+}
+
+/*!
+ Returns the key of the current item, as a long.
+*/
+
+long Q3GCacheIterator::getKeyInt() const
+{
+ Q3CacheItem *item = it->current();
+ return item ? (quintptr)item->key : 0;
+}
+
+/*!
+ Moves to the next item (postfix).
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::operator()()
+{
+ Q3CacheItem *item = it->operator()();
+ return item ? item->data : 0;
+}
+
+/*!
+ Moves to the next item (prefix).
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::operator++()
+{
+ Q3CacheItem *item = it->operator++();
+ return item ? item->data : 0;
+}
+
+/*!
+ Moves \a jump positions forward.
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::operator+=(uint jump)
+{
+ Q3CacheItem *item = it->operator+=(jump);
+ return item ? item->data : 0;
+}
+
+/*!
+ Moves to the previous item (prefix).
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::operator--()
+{
+ Q3CacheItem *item = it->operator--();
+ return item ? item->data : 0;
+}
+
+/*!
+ Moves \a jump positions backward.
+*/
+
+Q3PtrCollection::Item Q3GCacheIterator::operator-=(uint jump)
+{
+ Q3CacheItem *item = it->operator-=(jump);
+ return item ? item->data : 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3gcache.h b/src/qt3support/tools/q3gcache.h
new file mode 100644
index 0000000..7fabfa8
--- /dev/null
+++ b/src/qt3support/tools/q3gcache.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GCACHE_H
+#define Q3GCACHE_H
+
+#include <Qt3Support/q3ptrcollection.h>
+#include <Qt3Support/q3glist.h>
+#include <Qt3Support/q3gdict.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3CList; // internal classes
+class Q3CListIt;
+class Q3CDict;
+
+class Q_COMPAT_EXPORT Q3GCache : public Q3PtrCollection // generic LRU cache
+{
+friend class Q3GCacheIterator;
+protected:
+ enum KeyType { StringKey, AsciiKey, IntKey, PtrKey };
+ // identical to Q3GDict's, but PtrKey is not used at the moment
+
+ Q3GCache(int maxCost, uint size, KeyType kt, bool caseSensitive,
+ bool copyKeys);
+ Q3GCache(const Q3GCache &); // not allowed, calls fatal()
+ ~Q3GCache();
+ Q3GCache &operator=(const Q3GCache &); // not allowed, calls fatal()
+
+ uint count() const;
+ uint size() const;
+ int maxCost() const { return mCost; }
+ int totalCost() const { return tCost; }
+ void setMaxCost(int maxCost);
+ void clear();
+
+ bool insert_string(const QString &key, Q3PtrCollection::Item,
+ int cost, int priority);
+ bool insert_other(const char *key, Q3PtrCollection::Item,
+ int cost, int priority);
+ bool remove_string(const QString &key);
+ bool remove_other(const char *key);
+ Q3PtrCollection::Item take_string(const QString &key);
+ Q3PtrCollection::Item take_other(const char *key);
+
+ Q3PtrCollection::Item find_string(const QString &key, bool ref=true) const;
+ Q3PtrCollection::Item find_other(const char *key, bool ref=true) const;
+
+ void statistics() const;
+
+private:
+ bool makeRoomFor(int cost, int priority = -1);
+ KeyType keytype;
+ Q3CList *lruList;
+ Q3CDict *dict;
+ int mCost;
+ int tCost;
+ bool copyk;
+};
+
+
+class Q_COMPAT_EXPORT Q3GCacheIterator // generic cache iterator
+{
+protected:
+ Q3GCacheIterator(const Q3GCache &);
+ Q3GCacheIterator(const Q3GCacheIterator &);
+ ~Q3GCacheIterator();
+ Q3GCacheIterator &operator=(const Q3GCacheIterator &);
+
+ uint count() const;
+ bool atFirst() const;
+ bool atLast() const;
+ Q3PtrCollection::Item toFirst();
+ Q3PtrCollection::Item toLast();
+
+ Q3PtrCollection::Item get() const;
+ QString getKeyString() const;
+ const char *getKeyAscii() const;
+ long getKeyInt() const;
+
+ Q3PtrCollection::Item operator()();
+ Q3PtrCollection::Item operator++();
+ Q3PtrCollection::Item operator+=(uint);
+ Q3PtrCollection::Item operator--();
+ Q3PtrCollection::Item operator-=(uint);
+
+protected:
+ Q3CListIt *it; // iterator on cache list
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GCACHE_H
diff --git a/src/qt3support/tools/q3gdict.cpp b/src/qt3support/tools/q3gdict.cpp
new file mode 100644
index 0000000..e2a33b0
--- /dev/null
+++ b/src/qt3support/tools/q3gdict.cpp
@@ -0,0 +1,1154 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3gdict.h"
+#include "q3ptrlist.h"
+#include "qstring.h"
+#include "qdatastream.h"
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3GDict
+ \reentrant
+ \brief The Q3GDict class is an internal class for implementing QDict template classes.
+
+ \internal
+
+ Q3GDict is a strictly internal class that acts as a base class for the
+ \link collection.html collection classes\endlink QDict and QIntDict.
+
+ Q3GDict has some virtual functions that can be reimplemented to customize
+ the subclasses.
+ \list
+ \i read() reads a collection/dictionary item from a QDataStream.
+ \i write() writes a collection/dictionary item to a QDataStream.
+ \endlist
+ Normally, you do not have to reimplement any of these functions.
+*/
+
+static const int op_find = 0;
+static const int op_insert = 1;
+static const int op_replace = 2;
+
+
+class Q3GDItList : public Q3PtrList<Q3GDictIterator>
+{
+public:
+ Q3GDItList() : Q3PtrList<Q3GDictIterator>() {}
+ Q3GDItList(const Q3GDItList &list) : Q3PtrList<Q3GDictIterator>(list) {}
+ ~Q3GDItList() { clear(); }
+ Q3GDItList &operator=(const Q3GDItList &list)
+ { return (Q3GDItList&)Q3PtrList<Q3GDictIterator>::operator=(list); }
+};
+
+
+/*****************************************************************************
+ Default implementation of special and virtual functions
+ *****************************************************************************/
+
+/*!
+ Returns the hash key for \a key, when key is a string.
+*/
+
+int Q3GDict::hashKeyString(const QString &key)
+{
+#if defined(QT_CHECK_NULL)
+ if (key.isNull())
+ qWarning("Q3GDict::hashKeyString: Invalid null key");
+#endif
+ int i;
+ register uint h=0;
+ uint g;
+ const QChar *p = key.unicode();
+ if (cases) { // case sensitive
+ for (i=0; i<(int)key.length(); i++) {
+ h = (h<<4) + p[i].cell();
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ } else { // case insensitive
+ for (i=0; i<(int)key.length(); i++) {
+ h = (h<<4) + p[i].lower().cell();
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ }
+ int index = h;
+ if (index < 0) // adjust index to table size
+ index = -index;
+ return index;
+}
+
+/*!
+ Returns the hash key for \a key, which is a C string.
+*/
+
+int Q3GDict::hashKeyAscii(const char *key)
+{
+#if defined(QT_CHECK_NULL)
+ if (key == 0)
+ qWarning("Q3GDict::hashAsciiKey: Invalid null key");
+#endif
+ register const char *k = key;
+ register uint h=0;
+ uint g;
+ if (cases) { // case sensitive
+ while (*k) {
+ h = (h<<4) + *k++;
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ } else { // case insensitive
+ while (*k) {
+ h = (h<<4) + tolower((uchar) *k);
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ k++;
+ }
+ }
+ int index = h;
+ if (index < 0) // adjust index to table size
+ index = -index;
+ return index;
+}
+
+#ifndef QT_NO_DATASTREAM
+
+/*!
+ \overload
+ Reads a collection/dictionary item from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+QDataStream& Q3GDict::read(QDataStream &s, Q3PtrCollection::Item &item)
+{
+ item = 0;
+ return s;
+}
+
+/*!
+ \overload
+ Writes a collection/dictionary item to the stream \a s and returns a
+ reference to the stream.
+
+ \sa read()
+*/
+
+QDataStream& Q3GDict::write(QDataStream &s, Q3PtrCollection::Item) const
+{
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+/*****************************************************************************
+ Q3GDict member functions
+ *****************************************************************************/
+
+/*!
+ Constructs a dictionary.
+
+ \a len is the initial size of the dictionary.
+ The key type is \a kt which may be \c StringKey, \c AsciiKey,
+ \c IntKey or \c PtrKey. The case-sensitivity of lookups is set with
+ \a caseSensitive. Keys are copied if \a copyKeys is true.
+*/
+
+Q3GDict::Q3GDict(uint len, KeyType kt, bool caseSensitive, bool copyKeys)
+{
+ init(len, kt, caseSensitive, copyKeys);
+}
+
+
+void Q3GDict::init(uint len, KeyType kt, bool caseSensitive, bool copyKeys)
+{
+ vlen = len ? len : 17;
+ vec = new Q3BaseBucket *[ vlen ];
+
+ Q_CHECK_PTR(vec);
+ memset((char*)vec, 0, vlen*sizeof(Q3BaseBucket*));
+ numItems = 0;
+ iterators = 0;
+ // The caseSensitive and copyKey options don't make sense for
+ // all dict types.
+ switch ((keytype = (uint)kt)) {
+ case StringKey:
+ cases = caseSensitive;
+ copyk = false;
+ break;
+ case AsciiKey:
+ cases = caseSensitive;
+ copyk = copyKeys;
+ break;
+ default:
+ cases = false;
+ copyk = false;
+ break;
+ }
+}
+
+
+/*!
+ Constructs a copy of \a dict.
+*/
+
+Q3GDict::Q3GDict(const Q3GDict & dict)
+ : Q3PtrCollection(dict)
+{
+ init(dict.vlen, (KeyType)dict.keytype, dict.cases, dict.copyk);
+ Q3GDictIterator it(dict);
+ while (it.get()) { // copy from other dict
+ switch (keytype) {
+ case StringKey:
+ look_string(it.getKeyString(), it.get(), op_insert);
+ break;
+ case AsciiKey:
+ look_ascii(it.getKeyAscii(), it.get(), op_insert);
+ break;
+ case IntKey:
+ look_int(it.getKeyInt(), it.get(), op_insert);
+ break;
+ case PtrKey:
+ look_ptr(it.getKeyPtr(), it.get(), op_insert);
+ break;
+ }
+ ++it;
+ }
+}
+
+
+/*!
+ Removes all items from the dictionary and destroys it.
+*/
+
+Q3GDict::~Q3GDict()
+{
+ clear(); // delete everything
+ delete [] vec;
+ if (!iterators) // no iterators for this dict
+ return;
+ Q3GDictIterator *i = iterators->first();
+ while (i) { // notify all iterators that
+ i->dict = 0; // this dict is deleted
+ i = iterators->next();
+ }
+ delete iterators;
+}
+
+
+/*!
+ Assigns \a dict to this dictionary.
+*/
+
+Q3GDict &Q3GDict::operator=(const Q3GDict &dict)
+{
+ if (&dict == this)
+ return *this;
+ clear();
+ Q3GDictIterator it(dict);
+ while (it.get()) { // copy from other dict
+ switch (keytype) {
+ case StringKey:
+ look_string(it.getKeyString(), it.get(), op_insert);
+ break;
+ case AsciiKey:
+ look_ascii(it.getKeyAscii(), it.get(), op_insert);
+ break;
+ case IntKey:
+ look_int(it.getKeyInt(), it.get(), op_insert);
+ break;
+ case PtrKey:
+ look_ptr(it.getKeyPtr(), it.get(), op_insert);
+ break;
+ }
+ ++it;
+ }
+ return *this;
+}
+
+/*!
+ \fn uint Q3GDict::count() const
+
+ Returns the number of items in the dictionary.
+*/
+
+/*!
+ \fn uint Q3GDict::size() const
+
+ Returns the size of the hash array.
+*/
+
+/*!
+ The do-it-all function; \a op is one of op_find, op_insert, op_replace.
+ The key is \a key and the item is \a d.
+*/
+
+Q3PtrCollection::Item Q3GDict::look_string(const QString &key, Q3PtrCollection::Item d,
+ int op)
+{
+ Q3StringBucket *n = 0;
+ int index = hashKeyString(key) % vlen;
+ if (op == op_find) { // find
+ if (cases) {
+ n = (Q3StringBucket*)vec[index];
+ while(n != 0) {
+ if (key == n->getKey())
+ return n->getData(); // item found
+ n = (Q3StringBucket*)n->getNext();
+ }
+ } else {
+ QString k = key.lower();
+ n = (Q3StringBucket*)vec[index];
+ while(n != 0) {
+ if (k == n->getKey().lower())
+ return n->getData(); // item found
+ n = (Q3StringBucket*)n->getNext();
+ }
+ }
+ return 0; // not found
+ }
+ if (op == op_replace) { // replace
+ if (vec[index] != 0) // maybe something there
+ remove_string(key);
+ }
+ // op_insert or op_replace
+ n = new Q3StringBucket(key,newItem(d),vec[index]);
+ Q_CHECK_PTR(n);
+#if defined(QT_CHECK_NULL)
+ if (n->getData() == 0)
+ qWarning("QDict: Cannot insert null item");
+#endif
+ vec[index] = n;
+ numItems++;
+ return n->getData();
+}
+
+Q3PtrCollection::Item Q3GDict::look_ascii(const char *key, Q3PtrCollection::Item d, int op)
+{
+ Q3AsciiBucket *n;
+ int index = hashKeyAscii(key) % vlen;
+ if (op == op_find) { // find
+ if (cases) {
+ for (n=(Q3AsciiBucket*)vec[index]; n;
+ n=(Q3AsciiBucket*)n->getNext()) {
+ if (qstrcmp(n->getKey(),key) == 0)
+ return n->getData(); // item found
+ }
+ } else {
+ for (n=(Q3AsciiBucket*)vec[index]; n;
+ n=(Q3AsciiBucket*)n->getNext()) {
+ if (qstricmp(n->getKey(),key) == 0)
+ return n->getData(); // item found
+ }
+ }
+ return 0; // not found
+ }
+ if (op == op_replace) { // replace
+ if (vec[index] != 0) // maybe something there
+ remove_ascii(key);
+ }
+ // op_insert or op_replace
+ n = new Q3AsciiBucket(copyk ? qstrdup(key) : key,newItem(d),vec[index]);
+ Q_CHECK_PTR(n);
+#if defined(QT_CHECK_NULL)
+ if (n->getData() == 0)
+ qWarning("QAsciiDict: Cannot insert null item");
+#endif
+ vec[index] = n;
+ numItems++;
+ return n->getData();
+}
+
+Q3PtrCollection::Item Q3GDict::look_int(long key, Q3PtrCollection::Item d, int op)
+{
+ Q3IntBucket *n;
+ int index = (int)((ulong)key % vlen); // simple hash
+ if (op == op_find) { // find
+ for (n=(Q3IntBucket*)vec[index]; n;
+ n=(Q3IntBucket*)n->getNext()) {
+ if (n->getKey() == key)
+ return n->getData(); // item found
+ }
+ return 0; // not found
+ }
+ if (op == op_replace) { // replace
+ if (vec[index] != 0) // maybe something there
+ remove_int(key);
+ }
+ // op_insert or op_replace
+ n = new Q3IntBucket(key,newItem(d),vec[index]);
+ Q_CHECK_PTR(n);
+#if defined(QT_CHECK_NULL)
+ if (n->getData() == 0)
+ qWarning("QIntDict: Cannot insert null item");
+#endif
+ vec[index] = n;
+ numItems++;
+ return n->getData();
+}
+
+Q3PtrCollection::Item Q3GDict::look_ptr(void *key, Q3PtrCollection::Item d, int op)
+{
+ Q3PtrBucket *n;
+ int index = (int)((quintptr)key % vlen); // simple hash
+ if (op == op_find) { // find
+ for (n=(Q3PtrBucket*)vec[index]; n;
+ n=(Q3PtrBucket*)n->getNext()) {
+ if (n->getKey() == key)
+ return n->getData(); // item found
+ }
+ return 0; // not found
+ }
+ if (op == op_replace) { // replace
+ if (vec[index] != 0) // maybe something there
+ remove_ptr(key);
+ }
+ // op_insert or op_replace
+ n = new Q3PtrBucket(key,newItem(d),vec[index]);
+ Q_CHECK_PTR(n);
+#if defined(QT_CHECK_NULL)
+ if (n->getData() == 0)
+ qWarning("Q3PtrDict: Cannot insert null item");
+#endif
+ vec[index] = n;
+ numItems++;
+ return n->getData();
+}
+
+
+/*!
+ Changes the size of the hashtable to \a newsize.
+ The contents of the dictionary are preserved,
+ but all iterators on the dictionary become invalid.
+*/
+void Q3GDict::resize(uint newsize)
+{
+ // Save old information
+ Q3BaseBucket **old_vec = vec;
+ uint old_vlen = vlen;
+ bool old_copyk = copyk;
+
+ vec = new Q3BaseBucket *[vlen = newsize];
+ Q_CHECK_PTR(vec);
+ memset((char*)vec, 0, vlen*sizeof(Q3BaseBucket*));
+ numItems = 0;
+ copyk = false;
+
+ // Reinsert every item from vec, deleting vec as we go
+ for (uint index = 0; index < old_vlen; index++) {
+ switch (keytype) {
+ case StringKey:
+ {
+ Q3StringBucket *n=(Q3StringBucket *)old_vec[index];
+ while (n) {
+ look_string(n->getKey(), n->getData(), op_insert);
+ Q3StringBucket *t=(Q3StringBucket *)n->getNext();
+ delete n;
+ n = t;
+ }
+ }
+ break;
+ case AsciiKey:
+ {
+ Q3AsciiBucket *n=(Q3AsciiBucket *)old_vec[index];
+ while (n) {
+ look_ascii(n->getKey(), n->getData(), op_insert);
+ Q3AsciiBucket *t=(Q3AsciiBucket *)n->getNext();
+ delete n;
+ n = t;
+ }
+ }
+ break;
+ case IntKey:
+ {
+ Q3IntBucket *n=(Q3IntBucket *)old_vec[index];
+ while (n) {
+ look_int(n->getKey(), n->getData(), op_insert);
+ Q3IntBucket *t=(Q3IntBucket *)n->getNext();
+ delete n;
+ n = t;
+ }
+ }
+ break;
+ case PtrKey:
+ {
+ Q3PtrBucket *n=(Q3PtrBucket *)old_vec[index];
+ while (n) {
+ look_ptr(n->getKey(), n->getData(), op_insert);
+ Q3PtrBucket *t=(Q3PtrBucket *)n->getNext();
+ delete n;
+ n = t;
+ }
+ }
+ break;
+ }
+ }
+ delete [] old_vec;
+
+ // Restore state
+ copyk = old_copyk;
+
+ // Invalidate all iterators, since order is lost
+ if (iterators && iterators->count()) {
+ Q3GDictIterator *i = iterators->first();
+ while (i) {
+ i->toFirst();
+ i = iterators->next();
+ }
+ }
+}
+
+/*!
+ Unlinks the bucket with the specified key (and specified data pointer,
+ if it is set).
+*/
+
+void Q3GDict::unlink_common(int index, Q3BaseBucket *node, Q3BaseBucket *prev)
+{
+ if (iterators && iterators->count()) { // update iterators
+ Q3GDictIterator *i = iterators->first();
+ while (i) { // invalidate all iterators
+ if (i->curNode == node) // referring to pending node
+ i->operator++();
+ i = iterators->next();
+ }
+ }
+ if (prev) // unlink node
+ prev->setNext(node->getNext());
+ else
+ vec[index] = node->getNext();
+ numItems--;
+}
+
+Q3StringBucket *Q3GDict::unlink_string(const QString &key, Q3PtrCollection::Item d)
+{
+ if (numItems == 0) // nothing in dictionary
+ return 0;
+ Q3StringBucket *n;
+ Q3StringBucket *prev = 0;
+ int index = hashKeyString(key) % vlen;
+ if (cases) {
+ for (n=(Q3StringBucket*)vec[index]; n;
+ n=(Q3StringBucket*)n->getNext()) {
+ bool found = (key == n->getKey());
+ if (found && d)
+ found = (n->getData() == d);
+ if (found) {
+ unlink_common(index,n,prev);
+ return n;
+ }
+ prev = n;
+ }
+ } else {
+ QString k = key.lower();
+ for (n=(Q3StringBucket*)vec[index]; n;
+ n=(Q3StringBucket*)n->getNext()) {
+ bool found = (k == n->getKey().lower());
+ if (found && d)
+ found = (n->getData() == d);
+ if (found) {
+ unlink_common(index,n,prev);
+ return n;
+ }
+ prev = n;
+ }
+ }
+ return 0;
+}
+
+Q3AsciiBucket *Q3GDict::unlink_ascii(const char *key, Q3PtrCollection::Item d)
+{
+ if (numItems == 0) // nothing in dictionary
+ return 0;
+ Q3AsciiBucket *n;
+ Q3AsciiBucket *prev = 0;
+ int index = hashKeyAscii(key) % vlen;
+ for (n=(Q3AsciiBucket *)vec[index]; n; n=(Q3AsciiBucket *)n->getNext()) {
+ bool found = (cases ? qstrcmp(n->getKey(),key)
+ : qstricmp(n->getKey(),key)) == 0;
+ if (found && d)
+ found = (n->getData() == d);
+ if (found) {
+ unlink_common(index,n,prev);
+ return n;
+ }
+ prev = n;
+ }
+ return 0;
+}
+
+Q3IntBucket *Q3GDict::unlink_int(long key, Q3PtrCollection::Item d)
+{
+ if (numItems == 0) // nothing in dictionary
+ return 0;
+ Q3IntBucket *n;
+ Q3IntBucket *prev = 0;
+ int index = (int)((ulong)key % vlen);
+ for (n=(Q3IntBucket *)vec[index]; n; n=(Q3IntBucket *)n->getNext()) {
+ bool found = (n->getKey() == key);
+ if (found && d)
+ found = (n->getData() == d);
+ if (found) {
+ unlink_common(index,n,prev);
+ return n;
+ }
+ prev = n;
+ }
+ return 0;
+}
+
+Q3PtrBucket *Q3GDict::unlink_ptr(void *key, Q3PtrCollection::Item d)
+{
+ if (numItems == 0) // nothing in dictionary
+ return 0;
+ Q3PtrBucket *n;
+ Q3PtrBucket *prev = 0;
+ int index = (int)((quintptr)key % vlen);
+ for (n=(Q3PtrBucket *)vec[index]; n; n=(Q3PtrBucket *)n->getNext()) {
+ bool found = (n->getKey() == key);
+ if (found && d)
+ found = (n->getData() == d);
+ if (found) {
+ unlink_common(index,n,prev);
+ return n;
+ }
+ prev = n;
+ }
+ return 0;
+}
+
+
+/*!
+ Removes the item with the specified \a key. If \a item is not null,
+ the remove will match the \a item as well (used to remove an
+ item when several items have the same key).
+*/
+
+bool Q3GDict::remove_string(const QString &key, Q3PtrCollection::Item item)
+{
+ Q3StringBucket *n = unlink_string(key, item);
+ if (n) {
+ deleteItem(n->getData());
+ delete n;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool Q3GDict::remove_ascii(const char *key, Q3PtrCollection::Item item)
+{
+ Q3AsciiBucket *n = unlink_ascii(key, item);
+ if (n) {
+ if (copyk)
+ delete [] (char *)n->getKey();
+ deleteItem(n->getData());
+ delete n;
+ }
+ return n != 0;
+}
+
+bool Q3GDict::remove_int(long key, Q3PtrCollection::Item item)
+{
+ Q3IntBucket *n = unlink_int(key, item);
+ if (n) {
+ deleteItem(n->getData());
+ delete n;
+ }
+ return n != 0;
+}
+
+bool Q3GDict::remove_ptr(void *key, Q3PtrCollection::Item item)
+{
+ Q3PtrBucket *n = unlink_ptr(key, item);
+ if (n) {
+ deleteItem(n->getData());
+ delete n;
+ }
+ return n != 0;
+}
+
+Q3PtrCollection::Item Q3GDict::take_string(const QString &key)
+{
+ Q3StringBucket *n = unlink_string(key);
+ Item d;
+ if (n) {
+ d = n->getData();
+ delete n;
+ } else {
+ d = 0;
+ }
+ return d;
+}
+
+Q3PtrCollection::Item Q3GDict::take_ascii(const char *key)
+{
+ Q3AsciiBucket *n = unlink_ascii(key);
+ Item d;
+ if (n) {
+ if (copyk)
+ delete [] (char *)n->getKey();
+ d = n->getData();
+ delete n;
+ } else {
+ d = 0;
+ }
+ return d;
+}
+
+Q3PtrCollection::Item Q3GDict::take_int(long key)
+{
+ Q3IntBucket *n = unlink_int(key);
+ Item d;
+ if (n) {
+ d = n->getData();
+ delete n;
+ } else {
+ d = 0;
+ }
+ return d;
+}
+
+Q3PtrCollection::Item Q3GDict::take_ptr(void *key)
+{
+ Q3PtrBucket *n = unlink_ptr(key);
+ Item d;
+ if (n) {
+ d = n->getData();
+ delete n;
+ } else {
+ d = 0;
+ }
+ return d;
+}
+
+/*!
+ Removes all items from the dictionary.
+*/
+void Q3GDict::clear()
+{
+ if (!numItems)
+ return;
+ numItems = 0; // disable remove() function
+ for (uint j=0; j<vlen; j++) { // destroy hash table
+ if (vec[j]) {
+ switch (keytype) {
+ case StringKey:
+ {
+ Q3StringBucket *n=(Q3StringBucket *)vec[j];
+ while (n) {
+ Q3StringBucket *next = (Q3StringBucket*)n->getNext();
+ deleteItem(n->getData());
+ delete n;
+ n = next;
+ }
+ }
+ break;
+ case AsciiKey:
+ {
+ Q3AsciiBucket *n=(Q3AsciiBucket *)vec[j];
+ while (n) {
+ Q3AsciiBucket *next = (Q3AsciiBucket*)n->getNext();
+ if (copyk)
+ delete [] (char *)n->getKey();
+ deleteItem(n->getData());
+ delete n;
+ n = next;
+ }
+ }
+ break;
+ case IntKey:
+ {
+ Q3IntBucket *n=(Q3IntBucket *)vec[j];
+ while (n) {
+ Q3IntBucket *next = (Q3IntBucket*)n->getNext();
+ deleteItem(n->getData());
+ delete n;
+ n = next;
+ }
+ }
+ break;
+ case PtrKey:
+ {
+ Q3PtrBucket *n=(Q3PtrBucket *)vec[j];
+ while (n) {
+ Q3PtrBucket *next = (Q3PtrBucket*)n->getNext();
+ deleteItem(n->getData());
+ delete n;
+ n = next;
+ }
+ }
+ break;
+ }
+ vec[j] = 0; // detach list of buckets
+ }
+ }
+ if (iterators && iterators->count()) { // invalidate all iterators
+ Q3GDictIterator *i = iterators->first();
+ while (i) {
+ i->curNode = 0;
+ i = iterators->next();
+ }
+ }
+}
+
+/*!
+ Outputs debug statistics.
+*/
+void Q3GDict::statistics() const
+{
+#if defined(QT_DEBUG)
+ QString line;
+ line.fill(QLatin1Char('-'), 60);
+ double real, ideal;
+ qDebug("%s", line.ascii());
+ qDebug("DICTIONARY STATISTICS:");
+ if (count() == 0) {
+ qDebug("Empty!");
+ qDebug("%s", line.ascii());
+ return;
+ }
+ real = 0.0;
+ ideal = (float)count()/(2.0*size())*(count()+2.0*size()-1);
+ uint i = 0;
+ while (i<size()) {
+ Q3BaseBucket *n = vec[i];
+ int b = 0;
+ while (n) { // count number of buckets
+ b++;
+ n = n->getNext();
+ }
+ real = real + (double)b * ((double)b+1.0)/2.0;
+ char buf[80], *pbuf;
+ if (b > 78)
+ b = 78;
+ pbuf = buf;
+ while (b--)
+ *pbuf++ = '*';
+ *pbuf = '\0';
+ qDebug("%s", buf);
+ i++;
+ }
+ qDebug("Array size = %d", size());
+ qDebug("# items = %d", count());
+ qDebug("Real dist = %g", real);
+ qDebug("Rand dist = %g", ideal);
+ qDebug("Real/Rand = %g", real/ideal);
+ qDebug("%s", line.ascii());
+#endif // QT_DEBUG
+}
+
+
+/*****************************************************************************
+ Q3GDict stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+QDataStream &operator>>(QDataStream &s, Q3GDict &dict)
+{
+ return dict.read(s);
+}
+
+QDataStream &operator<<(QDataStream &s, const Q3GDict &dict)
+{
+ return dict.write(s);
+}
+
+#if defined(Q_CC_DEC) && defined(__alpha) && (__DECCXX_VER-0 >= 50190001)
+#pragma message disable narrowptr
+#endif
+
+/*!
+ Reads a dictionary from the stream \a s.
+*/
+
+QDataStream &Q3GDict::read(QDataStream &s)
+{
+ uint num;
+ s >> num; // read number of items
+ clear(); // clear dict
+ while (num--) { // read all items
+ Item d;
+ switch (keytype) {
+ case StringKey:
+ {
+ QString k;
+ s >> k;
+ read(s, d);
+ look_string(k, d, op_insert);
+ }
+ break;
+ case AsciiKey:
+ {
+ char *k;
+ s >> k;
+ read(s, d);
+ look_ascii(k, d, op_insert);
+ if (copyk)
+ delete [] k;
+ }
+ break;
+ case IntKey:
+ {
+ Q_UINT32 k;
+ s >> k;
+ read(s, d);
+ look_int(k, d, op_insert);
+ }
+ break;
+ case PtrKey:
+ {
+ Q_UINT32 k;
+ s >> k;
+ read(s, d);
+ // ### cannot insert 0 - this renders the thing
+ // useless since all pointers are written as 0,
+ // but hey, serializing pointers? can it be done
+ // at all, ever?
+ if (k)
+ look_ptr((void *)(ulong)k, d, op_insert);
+ }
+ break;
+ }
+ }
+ return s;
+}
+
+/*!
+ Writes the dictionary to the stream \a s.
+*/
+
+QDataStream& Q3GDict::write(QDataStream &s) const
+{
+ s << count(); // write number of items
+ uint i = 0;
+ while (i<size()) {
+ Q3BaseBucket *n = vec[i];
+ while (n) { // write all buckets
+ switch (keytype) {
+ case StringKey:
+ s << ((Q3StringBucket*)n)->getKey();
+ break;
+ case AsciiKey:
+ s << ((Q3AsciiBucket*)n)->getKey();
+ break;
+ case IntKey:
+ s << (Q_UINT32)((Q3IntBucket*)n)->getKey();
+ break;
+ case PtrKey:
+ s << (Q_UINT32)0; // ### cannot serialize a pointer
+ break;
+ }
+ write(s, n->getData()); // write data
+ n = n->getNext();
+ }
+ i++;
+ }
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+/*****************************************************************************
+ Q3GDictIterator member functions
+ *****************************************************************************/
+
+/*!
+ \class Q3GDictIterator
+ \reentrant
+ \brief The Q3GDictIterator class is an internal class for implementing QDictIterator and QIntDictIterator.
+
+ \internal
+
+ Q3GDictIterator is a strictly internal class that does the heavy work for
+ QDictIterator and QIntDictIterator.
+*/
+
+/*!
+ Constructs an iterator that operates on the dictionary \a d.
+*/
+
+Q3GDictIterator::Q3GDictIterator(const Q3GDict &d)
+{
+ dict = (Q3GDict *)&d; // get reference to dict
+ toFirst(); // set to first noe
+ if (!dict->iterators) {
+ dict->iterators = new Q3GDItList; // create iterator list
+ Q_CHECK_PTR(dict->iterators);
+ }
+ dict->iterators->append(this); // attach iterator to dict
+}
+
+/*!
+ Constructs a copy of the iterator \a it.
+*/
+
+Q3GDictIterator::Q3GDictIterator(const Q3GDictIterator &it)
+{
+ dict = it.dict;
+ curNode = it.curNode;
+ curIndex = it.curIndex;
+ if (dict)
+ dict->iterators->append(this); // attach iterator to dict
+}
+
+/*!
+ Assigns a copy of the iterator \a it and returns a reference to this
+ iterator.
+*/
+
+Q3GDictIterator &Q3GDictIterator::operator=(const Q3GDictIterator &it)
+{
+ if (dict) // detach from old dict
+ dict->iterators->removeRef(this);
+ dict = it.dict;
+ curNode = it.curNode;
+ curIndex = it.curIndex;
+ if (dict)
+ dict->iterators->append(this); // attach to new list
+ return *this;
+}
+
+/*!
+ Destroys the iterator.
+*/
+
+Q3GDictIterator::~Q3GDictIterator()
+{
+ if (dict) // detach iterator from dict
+ dict->iterators->removeRef(this);
+}
+
+
+/*!
+ Sets the iterator to point to the first item in the dictionary.
+*/
+
+Q3PtrCollection::Item Q3GDictIterator::toFirst()
+{
+ if (!dict) {
+#if defined(QT_CHECK_NULL)
+ qWarning("Q3GDictIterator::toFirst: Dictionary has been deleted");
+#endif
+ return 0;
+ }
+ if (dict->count() == 0) { // empty dictionary
+ curNode = 0;
+ return 0;
+ }
+ register uint i = 0;
+ register Q3BaseBucket **v = dict->vec;
+ while (!(*v++))
+ i++;
+ curNode = dict->vec[i];
+ curIndex = i;
+ return curNode->getData();
+}
+
+
+/*!
+ Moves to the next item (postfix).
+*/
+
+Q3PtrCollection::Item Q3GDictIterator::operator()()
+{
+ if (!dict) {
+#if defined(QT_CHECK_NULL)
+ qWarning("Q3GDictIterator::operator(): Dictionary has been deleted");
+#endif
+ return 0;
+ }
+ if (!curNode)
+ return 0;
+ Q3PtrCollection::Item d = curNode->getData();
+ this->operator++();
+ return d;
+}
+
+/*!
+ Moves to the next item (prefix).
+*/
+
+Q3PtrCollection::Item Q3GDictIterator::operator++()
+{
+ if (!dict) {
+#if defined(QT_CHECK_NULL)
+ qWarning("Q3GDictIterator::operator++: Dictionary has been deleted");
+#endif
+ return 0;
+ }
+ if (!curNode)
+ return 0;
+ curNode = curNode->getNext();
+ if (!curNode) { // no next bucket
+ register uint i = curIndex + 1; // look from next vec element
+ register Q3BaseBucket **v = &dict->vec[i];
+ while (i < dict->size() && !(*v++))
+ i++;
+ if (i == dict->size()) { // nothing found
+ curNode = 0;
+ return 0;
+ }
+ curNode = dict->vec[i];
+ curIndex = i;
+ }
+ return curNode->getData();
+}
+
+/*!
+ Moves \a jumps positions forward.
+*/
+
+Q3PtrCollection::Item Q3GDictIterator::operator+=(uint jumps)
+{
+ while (curNode && jumps--)
+ operator++();
+ return curNode ? curNode->getData() : 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3gdict.h b/src/qt3support/tools/q3gdict.h
new file mode 100644
index 0000000..da1cd4f
--- /dev/null
+++ b/src/qt3support/tools/q3gdict.h
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GDICT_H
+#define Q3GDICT_H
+
+#include <Qt3Support/q3ptrcollection.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3GDictIterator;
+class Q3GDItList;
+
+
+class Q3BaseBucket // internal dict node
+{
+public:
+ Q3PtrCollection::Item getData() { return data; }
+ Q3PtrCollection::Item setData( Q3PtrCollection::Item d ) { return data = d; }
+ Q3BaseBucket *getNext() { return next; }
+ void setNext( Q3BaseBucket *n) { next = n; }
+protected:
+ Q3BaseBucket( Q3PtrCollection::Item d, Q3BaseBucket *n ) : data(d), next(n) {}
+ Q3PtrCollection::Item data;
+ Q3BaseBucket *next;
+};
+
+class Q3StringBucket : public Q3BaseBucket
+{
+public:
+ Q3StringBucket( const QString &k, Q3PtrCollection::Item d, Q3BaseBucket *n )
+ : Q3BaseBucket(d,n), key(k) {}
+ const QString &getKey() const { return key; }
+private:
+ QString key;
+};
+
+class Q3AsciiBucket : public Q3BaseBucket
+{
+public:
+ Q3AsciiBucket( const char *k, Q3PtrCollection::Item d, Q3BaseBucket *n )
+ : Q3BaseBucket(d,n), key(k) {}
+ const char *getKey() const { return key; }
+private:
+ const char *key;
+};
+
+class Q3IntBucket : public Q3BaseBucket
+{
+public:
+ Q3IntBucket( long k, Q3PtrCollection::Item d, Q3BaseBucket *n )
+ : Q3BaseBucket(d,n), key(k) {}
+ long getKey() const { return key; }
+private:
+ long key;
+};
+
+class Q3PtrBucket : public Q3BaseBucket
+{
+public:
+ Q3PtrBucket( void *k, Q3PtrCollection::Item d, Q3BaseBucket *n )
+ : Q3BaseBucket(d,n), key(k) {}
+ void *getKey() const { return key; }
+private:
+ void *key;
+};
+
+
+class Q_COMPAT_EXPORT Q3GDict : public Q3PtrCollection // generic dictionary class
+{
+public:
+ uint count() const { return numItems; }
+ uint size() const { return vlen; }
+ Q3PtrCollection::Item look_string( const QString& key, Q3PtrCollection::Item,
+ int );
+ Q3PtrCollection::Item look_ascii( const char *key, Q3PtrCollection::Item, int );
+ Q3PtrCollection::Item look_int( long key, Q3PtrCollection::Item, int );
+ Q3PtrCollection::Item look_ptr( void *key, Q3PtrCollection::Item, int );
+#ifndef QT_NO_DATASTREAM
+ QDataStream &read( QDataStream & );
+ QDataStream &write( QDataStream & ) const;
+#endif
+protected:
+ enum KeyType { StringKey, AsciiKey, IntKey, PtrKey };
+
+ Q3GDict( uint len, KeyType kt, bool cs, bool ck );
+ Q3GDict( const Q3GDict & );
+ ~Q3GDict();
+
+ Q3GDict &operator=( const Q3GDict & );
+
+ bool remove_string( const QString &key, Q3PtrCollection::Item item=0 );
+ bool remove_ascii( const char *key, Q3PtrCollection::Item item=0 );
+ bool remove_int( long key, Q3PtrCollection::Item item=0 );
+ bool remove_ptr( void *key, Q3PtrCollection::Item item=0 );
+ Q3PtrCollection::Item take_string( const QString &key );
+ Q3PtrCollection::Item take_ascii( const char *key );
+ Q3PtrCollection::Item take_int( long key );
+ Q3PtrCollection::Item take_ptr( void *key );
+
+ void clear();
+ void resize( uint );
+
+ int hashKeyString( const QString & );
+ int hashKeyAscii( const char * );
+
+ void statistics() const;
+
+#ifndef QT_NO_DATASTREAM
+ virtual QDataStream &read( QDataStream &, Q3PtrCollection::Item & );
+ virtual QDataStream &write( QDataStream &, Q3PtrCollection::Item ) const;
+#endif
+private:
+ Q3BaseBucket **vec;
+ uint vlen;
+ uint numItems;
+ uint keytype : 2;
+ uint cases : 1;
+ uint copyk : 1;
+ Q3GDItList *iterators;
+ void unlink_common( int, Q3BaseBucket *, Q3BaseBucket * );
+ Q3StringBucket *unlink_string( const QString &,
+ Q3PtrCollection::Item item = 0 );
+ Q3AsciiBucket *unlink_ascii( const char *, Q3PtrCollection::Item item = 0 );
+ Q3IntBucket *unlink_int( long, Q3PtrCollection::Item item = 0 );
+ Q3PtrBucket *unlink_ptr( void *, Q3PtrCollection::Item item = 0 );
+ void init( uint, KeyType, bool, bool );
+ friend class Q3GDictIterator;
+};
+
+
+class Q_COMPAT_EXPORT Q3GDictIterator // generic dictionary iterator
+{
+friend class Q3GDict;
+public:
+ Q3GDictIterator( const Q3GDict & );
+ Q3GDictIterator( const Q3GDictIterator & );
+ Q3GDictIterator &operator=( const Q3GDictIterator & );
+ ~Q3GDictIterator();
+
+ Q3PtrCollection::Item toFirst();
+
+ Q3PtrCollection::Item get() const;
+ QString getKeyString() const;
+ const char *getKeyAscii() const;
+ long getKeyInt() const;
+ void *getKeyPtr() const;
+
+ Q3PtrCollection::Item operator()();
+ Q3PtrCollection::Item operator++();
+ Q3PtrCollection::Item operator+=(uint);
+
+protected:
+ Q3GDict *dict;
+
+private:
+ Q3BaseBucket *curNode;
+ uint curIndex;
+};
+
+inline Q3PtrCollection::Item Q3GDictIterator::get() const
+{
+ return curNode ? curNode->getData() : 0;
+}
+
+inline QString Q3GDictIterator::getKeyString() const
+{
+ return curNode ? ((Q3StringBucket*)curNode)->getKey() : QString();
+}
+
+inline const char *Q3GDictIterator::getKeyAscii() const
+{
+ return curNode ? ((Q3AsciiBucket*)curNode)->getKey() : 0;
+}
+
+inline long Q3GDictIterator::getKeyInt() const
+{
+ return curNode ? ((Q3IntBucket*)curNode)->getKey() : 0;
+}
+
+inline void *Q3GDictIterator::getKeyPtr() const
+{
+ return curNode ? ((Q3PtrBucket*)curNode)->getKey() : 0;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GDICT_H
diff --git a/src/qt3support/tools/q3glist.cpp b/src/qt3support/tools/q3glist.cpp
new file mode 100644
index 0000000..8067b63
--- /dev/null
+++ b/src/qt3support/tools/q3glist.cpp
@@ -0,0 +1,1270 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3glist.h"
+#include "q3gvector.h"
+#include "qdatastream.h"
+#include "q3valuelist.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3LNode
+ \reentrant
+ \brief The Q3LNode class is an internal class for the Q3PtrList template collection.
+
+ \internal
+
+ Q3LNode is a doubly-linked list node. It has three pointers:
+ \list 1
+ \i Pointer to the previous node.
+ \i Pointer to the next node.
+ \i Pointer to the actual data.
+ \endlist
+
+ It might sometimes be practical to have direct access to the list nodes
+ in a Q3PtrList, but it is seldom required.
+
+ Be very careful if you want to access the list nodes. The heap can
+ easily get corrupted if you make a mistake.
+
+ \sa Q3PtrList::currentNode(), Q3PtrList::removeNode(), Q3PtrList::takeNode()
+*/
+
+/*!
+ \fn Q3PtrCollection::Item Q3LNode::getData()
+ Returns a pointer (\c void*) to the actual data in the list node.
+*/
+
+
+/*!
+ \class Q3GList
+ \reentrant
+ \brief The Q3GList class is an internal class for implementing Qt collection classes.
+
+ \internal
+
+ Q3GList is a strictly internal class that acts as a base class for
+ several collection classes; Q3PtrList, Q3PtrQueue and Q3PtrStack.
+
+ Q3GList has some virtual functions that can be reimplemented to
+ customize the subclasses, namely compareItems(), read() and
+ write. Normally, you do not have to reimplement any of these
+ functions. If you still want to reimplement them, see the QStrList
+ class (qstrlist.h) for an example.
+*/
+
+
+/* Internal helper class for Q3GList. Contains some optimization for
+ the typically case where only one iterators is activre on the list.
+ */
+class Q3GListIteratorList
+{
+public:
+ Q3GListIteratorList()
+ : list(0), iterator(0) {
+ }
+ ~Q3GListIteratorList() {
+ notifyClear( true );
+ delete list;
+ }
+
+ void add( Q3GListIterator* i ) {
+ if ( !iterator ) {
+ iterator = i;
+ } else if ( list ) {
+ list->push_front( i );
+ } else {
+ list = new Q3ValueList<Q3GListIterator*>;
+ list->push_front( i );
+ }
+ }
+
+ void remove( Q3GListIterator* i ) {
+ if ( iterator == i ) {
+ iterator = 0;
+ } else if ( list ) {
+ list->remove( i );
+ if ( list->isEmpty() ) {
+ delete list;
+ list = 0;
+ }
+ }
+ }
+
+ void notifyClear( bool zeroList ) {
+ if ( iterator ) {
+ if ( zeroList )
+ iterator->list = 0;
+ iterator->curNode = 0;
+ }
+ if ( list ) {
+ for ( Q3ValueList<Q3GListIterator*>::Iterator i = list->begin(); i != list->end(); ++i ) {
+ if ( zeroList )
+ (*i)->list = 0;
+ (*i)->curNode = 0;
+ }
+ }
+ }
+
+ void notifyRemove( Q3LNode* n, Q3LNode* curNode ) {
+ if ( iterator ) {
+ if ( iterator->curNode == n )
+ iterator->curNode = curNode;
+ }
+ if ( list ) {
+ for ( Q3ValueList<Q3GListIterator*>::Iterator i = list->begin(); i != list->end(); ++i ) {
+ if ( (*i)->curNode == n )
+ (*i)->curNode = curNode;
+ }
+ }
+ }
+
+private:
+ Q3ValueList<Q3GListIterator*>* list;
+ Q3GListIterator* iterator;
+};
+
+
+
+/*****************************************************************************
+ Default implementation of virtual functions
+ *****************************************************************************/
+
+/*!
+ Documented as Q3PtrList::compareItems().
+
+ Compares \a item1 with \a item2.
+*/
+int Q3GList::compareItems( Q3PtrCollection::Item item1, Q3PtrCollection::Item item2 )
+{
+ return item1 != item2; // compare pointers
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \overload
+ Reads a collection/list item from the stream \a s and returns a reference
+ to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+QDataStream &Q3GList::read( QDataStream &s, Q3PtrCollection::Item &item )
+{
+ item = 0;
+ return s;
+}
+
+/*!
+ \overload
+ Writes a collection/list item to the stream \a s and
+ returns a reference to the stream.
+
+ The default implementation does nothing.
+
+ \sa read()
+*/
+
+QDataStream &Q3GList::write( QDataStream &s, Q3PtrCollection::Item ) const
+{
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+/*****************************************************************************
+ Q3GList member functions
+ *****************************************************************************/
+
+/*!
+ Constructs an empty list.
+*/
+
+Q3GList::Q3GList()
+{
+ firstNode = lastNode = curNode = 0; // initialize list
+ numNodes = 0;
+ curIndex = -1;
+ iterators = 0; // initialize iterator list
+}
+
+/*!
+ Constructs a copy of \a list.
+*/
+
+Q3GList::Q3GList( const Q3GList & list )
+ : Q3PtrCollection( list )
+{
+ firstNode = lastNode = curNode = 0; // initialize list
+ numNodes = 0;
+ curIndex = -1;
+ iterators = 0; // initialize iterator list
+ Q3LNode *n = list.firstNode;
+ while ( n ) { // copy all items from list
+ append( n->data );
+ n = n->next;
+ }
+}
+
+/*!
+ Removes all items from the list and destroys the list.
+*/
+
+Q3GList::~Q3GList()
+{
+ clear();
+ delete iterators;
+ // Workaround for GCC 2.7.* bug. Compiler constructs 'static' Q3GList
+ // instances twice on the same address and therefore tries to destruct
+ // twice on the same address! This is insane but let's try not to crash
+ // here.
+ iterators = 0;
+}
+
+
+/*!
+ Assigns \a list to this list.
+*/
+
+Q3GList& Q3GList::operator=( const Q3GList &list )
+{
+ if ( &list == this )
+ return *this;
+
+ clear();
+ if ( list.count() > 0 ) {
+ Q3LNode *n = list.firstNode;
+ while ( n ) { // copy all items from list
+ append( n->data );
+ n = n->next;
+ }
+ curNode = firstNode;
+ curIndex = 0;
+ }
+ return *this;
+}
+
+/*!
+ Compares this list with \a list. Returns true if the lists
+ contain the same data, otherwise false.
+*/
+
+bool Q3GList::operator==( const Q3GList &list ) const
+{
+ if ( count() != list.count() )
+ return false;
+
+ if ( count() == 0 )
+ return true;
+
+ Q3LNode *n1 = firstNode;
+ Q3LNode *n2 = list.firstNode;
+ while ( n1 && n2 ) {
+ // should be mutable
+ if ( ( (Q3GList*)this )->compareItems( n1->data, n2->data ) != 0 )
+ return false;
+ n1 = n1->next;
+ n2 = n2->next;
+ }
+
+ return true;
+}
+
+/*!
+ \fn uint Q3GList::count() const
+
+ Returns the number of items in the list.
+*/
+
+
+/*!
+ Returns the node at position \a index. Sets this node to current.
+*/
+
+Q3LNode *Q3GList::locate( uint index )
+{
+ if ( index == (uint)curIndex ) // current node ?
+ return curNode;
+ if ( !curNode && firstNode ) { // set current node
+ curNode = firstNode;
+ curIndex = 0;
+ }
+ register Q3LNode *node;
+ int distance = index - curIndex; // node distance to cur node
+ bool forward; // direction to traverse
+
+ if ( index >= numNodes )
+ return 0;
+
+ if ( distance < 0 )
+ distance = -distance;
+ if ( (uint)distance < index && (uint)distance < numNodes - index ) {
+ node = curNode; // start from current node
+ forward = index > (uint)curIndex;
+ } else if ( index < numNodes - index ) { // start from first node
+ node = firstNode;
+ distance = index;
+ forward = true;
+ } else { // start from last node
+ node = lastNode;
+ distance = numNodes - index - 1;
+ if ( distance < 0 )
+ distance = 0;
+ forward = false;
+ }
+ if ( forward ) { // now run through nodes
+ while ( distance-- )
+ node = node->next;
+ } else {
+ while ( distance-- )
+ node = node->prev;
+ }
+ curIndex = index; // must update index
+ return curNode = node;
+}
+
+
+/*!
+ Inserts item \a d at its sorted position in the list.
+*/
+
+void Q3GList::inSort( Q3PtrCollection::Item d )
+{
+ int index = 0;
+ register Q3LNode *n = firstNode;
+ while ( n && compareItems(n->data,d) < 0 ){ // find position in list
+ n = n->next;
+ index++;
+ }
+ insertAt( index, d );
+}
+
+
+/*!
+ Inserts item \a d at the start of the list.
+*/
+
+void Q3GList::prepend( Q3PtrCollection::Item d )
+{
+ register Q3LNode *n = new Q3LNode( newItem(d) );
+ Q_CHECK_PTR( n );
+ n->prev = 0;
+ if ( (n->next = firstNode) ) // list is not empty
+ firstNode->prev = n;
+ else // initialize list
+ lastNode = n;
+ firstNode = curNode = n; // curNode affected
+ numNodes++;
+ curIndex = 0;
+}
+
+
+/*!
+ Inserts item \a d at the end of the list.
+*/
+
+void Q3GList::append( Q3PtrCollection::Item d )
+{
+ register Q3LNode *n = new Q3LNode( newItem(d) );
+ Q_CHECK_PTR( n );
+ n->next = 0;
+ if ( (n->prev = lastNode) ) // list is not empty
+ lastNode->next = n;
+ else // initialize list
+ firstNode = n;
+ lastNode = curNode = n; // curNode affected
+ curIndex = numNodes;
+ numNodes++;
+}
+
+
+/*!
+ Inserts item \a d at position \a index in the list.
+*/
+
+bool Q3GList::insertAt( uint index, Q3PtrCollection::Item d )
+{
+ if ( index == 0 ) {
+ prepend( d );
+ return true;
+ } else if ( index == numNodes ) {
+ append( d );
+ return true;
+ }
+ Q3LNode *nextNode = locate( index );
+ if ( !nextNode )
+ return false;
+ Q3LNode *prevNode = nextNode->prev;
+ register Q3LNode *n = new Q3LNode( newItem(d) );
+ Q_CHECK_PTR( n );
+ nextNode->prev = n;
+ prevNode->next = n;
+ n->prev = prevNode; // link new node into list
+ n->next = nextNode;
+ curNode = n; // curIndex set by locate()
+ numNodes++;
+ return true;
+}
+
+
+/*!
+ Relinks node \a n and makes it the first node in the list.
+*/
+
+void Q3GList::relinkNode( Q3LNode *n )
+{
+ if ( n == firstNode ) // already first
+ return;
+ curNode = n;
+ unlink();
+ n->prev = 0;
+ if ( (n->next = firstNode) ) // list is not empty
+ firstNode->prev = n;
+ else // initialize list
+ lastNode = n;
+ firstNode = curNode = n; // curNode affected
+ numNodes++;
+ curIndex = 0;
+}
+
+
+/*!
+ Unlinks the current list node and returns a pointer to this node.
+*/
+
+Q3LNode *Q3GList::unlink()
+{
+ if ( curNode == 0 ) // null current node
+ return 0;
+ register Q3LNode *n = curNode; // unlink this node
+ if ( n == firstNode ) { // removing first node ?
+ if ( (firstNode = n->next) ) {
+ firstNode->prev = 0;
+ } else {
+ lastNode = curNode = 0; // list becomes empty
+ curIndex = -1;
+ }
+ } else {
+ if ( n == lastNode ) { // removing last node ?
+ lastNode = n->prev;
+ lastNode->next = 0;
+ } else { // neither last nor first node
+ n->prev->next = n->next;
+ n->next->prev = n->prev;
+ }
+ }
+
+ if ( n->next ) { // change current node
+ curNode = n->next;
+ } else if ( n->prev ) {
+ curNode = n->prev;
+ curIndex--;
+ }
+
+ if ( iterators )
+ iterators->notifyRemove( n, curNode );
+ numNodes--;
+ return n;
+}
+
+
+/*!
+ Removes the node \a n from the list.
+*/
+
+bool Q3GList::removeNode( Q3LNode *n )
+{
+#if defined(QT_CHECK_NULL)
+ if ( n == 0 || (n->prev && n->prev->next != n) ||
+ (n->next && n->next->prev != n) ) {
+ qWarning( "Q3GList::removeNode: Corrupted node" );
+ return false;
+ }
+#endif
+ curNode = n;
+ unlink(); // unlink node
+ deleteItem( n->data ); // deallocate this node
+ delete n;
+ curNode = firstNode;
+ curIndex = curNode ? 0 : -1;
+ return true;
+}
+
+/*!
+ Removes the item \a d from the list. Uses compareItems() to find the item.
+
+ If \a d is 0, removes the current item.
+*/
+
+bool Q3GList::remove( Q3PtrCollection::Item d )
+{
+ if ( d && find(d) == -1 )
+ return false;
+ Q3LNode *n = unlink();
+ if ( !n )
+ return false;
+ deleteItem( n->data );
+ delete n;
+ return true;
+}
+
+/*!
+ Removes the item \a d from the list.
+*/
+
+bool Q3GList::removeRef( Q3PtrCollection::Item d )
+{
+ if ( findRef(d) == -1 )
+ return false;
+ Q3LNode *n = unlink();
+ if ( !n )
+ return false;
+ deleteItem( n->data );
+ delete n;
+ return true;
+}
+
+/*!
+ \fn bool Q3GList::removeFirst()
+
+ Removes the first item in the list.
+*/
+
+/*!
+ \fn bool Q3GList::removeLast()
+
+ Removes the last item in the list.
+*/
+
+/*!
+ Removes the item at position \a index from the list.
+*/
+
+bool Q3GList::removeAt( uint index )
+{
+ if ( !locate(index) )
+ return false;
+ Q3LNode *n = unlink();
+ if ( !n )
+ return false;
+ deleteItem( n->data );
+ delete n;
+ return true;
+}
+
+
+/*!
+ Replaces the item at index \a index with \a d.
+*/
+bool Q3GList::replaceAt( uint index, Q3PtrCollection::Item d )
+{
+ Q3LNode *n = locate( index );
+ if ( !n )
+ return false;
+ if ( n->data != d ) {
+ deleteItem( n->data );
+ n->data = newItem( d );
+ }
+ return true;
+}
+
+
+
+/*!
+ Takes the node \a n out of the list.
+*/
+
+Q3PtrCollection::Item Q3GList::takeNode( Q3LNode *n )
+{
+#if defined(QT_CHECK_NULL)
+ if ( n == 0 || (n->prev && n->prev->next != n) ||
+ (n->next && n->next->prev != n) ) {
+ qWarning( "Q3GList::takeNode: Corrupted node" );
+ return 0;
+ }
+#endif
+ curNode = n;
+ unlink(); // unlink node
+ Item d = n->data;
+ delete n; // delete the node, not data
+ curNode = firstNode;
+ curIndex = curNode ? 0 : -1;
+ return d;
+}
+
+/*!
+ Takes the current item out of the list.
+*/
+
+Q3PtrCollection::Item Q3GList::take()
+{
+ Q3LNode *n = unlink(); // unlink node
+ Item d = n ? n->data : 0;
+ delete n; // delete node, keep contents
+ return d;
+}
+
+/*!
+ Takes the item at position \a index out of the list.
+*/
+
+Q3PtrCollection::Item Q3GList::takeAt( uint index )
+{
+ if ( !locate(index) )
+ return 0;
+ Q3LNode *n = unlink(); // unlink node
+ Item d = n ? n->data : 0;
+ delete n; // delete node, keep contents
+ return d;
+}
+
+/*!
+ Takes the first item out of the list.
+*/
+
+Q3PtrCollection::Item Q3GList::takeFirst()
+{
+ first();
+ Q3LNode *n = unlink(); // unlink node
+ Item d = n ? n->data : 0;
+ delete n;
+ return d;
+}
+
+/*!
+ Takes the last item out of the list.
+*/
+
+Q3PtrCollection::Item Q3GList::takeLast()
+{
+ last();
+ Q3LNode *n = unlink(); // unlink node
+ Item d = n ? n->data : 0;
+ delete n;
+ return d;
+}
+
+
+/*!
+ Removes all items from the list.
+*/
+
+void Q3GList::clear()
+{
+ register Q3LNode *n = firstNode;
+
+ firstNode = lastNode = curNode = 0; // initialize list
+ numNodes = 0;
+ curIndex = -1;
+
+ if ( iterators )
+ iterators->notifyClear( false );
+
+ Q3LNode *prevNode;
+ while ( n ) { // for all nodes ...
+ deleteItem( n->data ); // deallocate data
+ prevNode = n;
+ n = n->next;
+ delete prevNode; // deallocate node
+ }
+}
+
+
+/*!
+ Finds item \a d in the list. If \a fromStart is true the search
+ begins at the first node; otherwise it begins at the current node.
+*/
+
+int Q3GList::findRef( Q3PtrCollection::Item d, bool fromStart )
+{
+ register Q3LNode *n;
+ int index;
+ if ( fromStart ) { // start from first node
+ n = firstNode;
+ index = 0;
+ } else { // start from current node
+ n = curNode;
+ index = curIndex;
+ }
+ while ( n && n->data != d ) { // find exact match
+ n = n->next;
+ index++;
+ }
+ curNode = n;
+ curIndex = n ? index : -1;
+ return curIndex; // return position of item
+}
+
+/*!
+ Finds item \a d in the list using compareItems(). If \a fromStart is
+ true the search begins at the first node; otherwise it begins at the
+ current node.
+*/
+
+int Q3GList::find( Q3PtrCollection::Item d, bool fromStart )
+{
+ register Q3LNode *n;
+ int index;
+ if ( fromStart ) { // start from first node
+ n = firstNode;
+ index = 0;
+ } else { // start from current node
+ n = curNode;
+ index = curIndex;
+ }
+ while ( n && compareItems(n->data,d) ){ // find equal match
+ n = n->next;
+ index++;
+ }
+ curNode = n;
+ curIndex = n ? index : -1;
+ return curIndex; // return position of item
+}
+
+
+/*!
+ Counts the number item \a d occurs in the list.
+*/
+
+uint Q3GList::containsRef( Q3PtrCollection::Item d ) const
+{
+ register Q3LNode *n = firstNode;
+ uint count = 0;
+ while ( n ) { // for all nodes...
+ if ( n->data == d ) // count # exact matches
+ count++;
+ n = n->next;
+ }
+ return count;
+}
+
+/*!
+ Counts the number of times item \a d occurs in the list. Uses
+ compareItems().
+*/
+
+uint Q3GList::contains( Q3PtrCollection::Item d ) const
+{
+ register Q3LNode *n = firstNode;
+ uint count = 0;
+ Q3GList *that = (Q3GList*)this; // mutable for compareItems()
+ while ( n ) { // for all nodes...
+ if ( !that->compareItems(n->data,d) ) // count # equal matches
+ count++;
+ n = n->next;
+ }
+ return count;
+}
+
+
+/*!
+ \fn Q3PtrCollection::Item Q3GList::at( uint index )
+ \overload
+
+ Sets the item at position \a index to the current item.
+*/
+
+/*!
+ \fn int Q3GList::at() const
+
+ Returns the current index.
+*/
+
+/*!
+ \fn Q3LNode *Q3GList::currentNode() const
+
+ Returns the current node.
+*/
+
+/*!
+ \fn Q3PtrCollection::Item Q3GList::get() const
+
+ Returns the current item.
+*/
+
+/*!
+ \fn Q3PtrCollection::Item Q3GList::cfirst() const
+
+ Returns the first item in the list.
+*/
+
+/*!
+ \fn Q3PtrCollection::Item Q3GList::clast() const
+
+ Returns the last item in the list.
+*/
+
+
+/*!
+ Returns the first list item. Sets this to current.
+*/
+
+Q3PtrCollection::Item Q3GList::first()
+{
+ if ( firstNode ) {
+ curIndex = 0;
+ return (curNode=firstNode)->data;
+ }
+ return 0;
+}
+
+/*!
+ Returns the last list item. Sets this to current.
+*/
+
+Q3PtrCollection::Item Q3GList::last()
+{
+ if ( lastNode ) {
+ curIndex = numNodes-1;
+ return (curNode=lastNode)->data;
+ }
+ return 0;
+}
+
+/*!
+ Returns the next list item (after current). Sets this to current.
+*/
+
+Q3PtrCollection::Item Q3GList::next()
+{
+ if ( curNode ) {
+ if ( curNode->next ) {
+ curIndex++;
+ curNode = curNode->next;
+ return curNode->data;
+ }
+ curIndex = -1;
+ curNode = 0;
+ }
+ return 0;
+}
+
+/*!
+ Returns the previous list item (before current). Sets this to current.
+*/
+
+Q3PtrCollection::Item Q3GList::prev()
+{
+ if ( curNode ) {
+ if ( curNode->prev ) {
+ curIndex--;
+ curNode = curNode->prev;
+ return curNode->data;
+ }
+ curIndex = -1;
+ curNode = 0;
+ }
+ return 0;
+}
+
+
+/*!
+ Converts the list to a vector, \a vector.
+*/
+
+void Q3GList::toVector( Q3GVector *vector ) const
+{
+ vector->clear();
+ if ( !vector->resize( count() ) )
+ return;
+ register Q3LNode *n = firstNode;
+ uint i = 0;
+ while ( n ) {
+ vector->insert( i, n->data );
+ n = n->next;
+ i++;
+ }
+}
+
+void Q3GList::heapSortPushDown( Q3PtrCollection::Item* heap, int first, int last )
+{
+ int r = first;
+ while( r <= last/2 ) {
+ // Node r has only one child ?
+ if ( last == 2*r ) {
+ // Need for swapping ?
+ if ( compareItems( heap[r], heap[ 2*r ] ) > 0 ) {
+ Q3PtrCollection::Item tmp = heap[r];
+ heap[ r ] = heap[ 2*r ];
+ heap[ 2*r ] = tmp;
+ }
+ // That's it ...
+ r = last;
+ } else {
+ // Node has two children
+ if ( compareItems( heap[r], heap[ 2*r ] ) > 0 &&
+ compareItems( heap[ 2*r ], heap[ 2*r+1 ] ) <= 0 ) {
+ // Swap with left child
+ Q3PtrCollection::Item tmp = heap[r];
+ heap[ r ] = heap[ 2*r ];
+ heap[ 2*r ] = tmp;
+ r *= 2;
+ } else if ( compareItems( heap[r], heap[ 2*r+1 ] ) > 0 &&
+ compareItems( heap[ 2*r+1 ], heap[ 2*r ] ) < 0 ) {
+ // Swap with right child
+ Q3PtrCollection::Item tmp = heap[r];
+ heap[ r ] = heap[ 2*r+1 ];
+ heap[ 2*r+1 ] = tmp;
+ r = 2*r+1;
+ } else {
+ // We are done
+ r = last;
+ }
+ }
+ }
+}
+
+
+/*! Sorts the list by the result of the virtual compareItems() function.
+
+ The Heap-Sort algorithm is used for sorting. It sorts n items with
+ O(n*log n) compares. This is the asymptotic optimal solution of the
+ sorting problem.
+*/
+
+void Q3GList::sort()
+{
+ uint n = count();
+ if ( n < 2 )
+ return;
+
+ // Create the heap
+ Q3PtrCollection::Item* realheap = new Q3PtrCollection::Item[ n ];
+ // Wow, what a fake. But I want the heap to be indexed as 1...n
+ Q3PtrCollection::Item* heap = realheap - 1;
+ int size = 0;
+ Q3LNode* insert = firstNode;
+ for( ; insert != 0; insert = insert->next ) {
+ heap[++size] = insert->data;
+ int i = size;
+ while( i > 1 && compareItems( heap[i], heap[ i / 2 ] ) < 0 ) {
+ Q3PtrCollection::Item tmp = heap[ i ];
+ heap[ i ] = heap[ i/2 ];
+ heap[ i/2 ] = tmp;
+ i /= 2;
+ }
+ }
+
+ insert = firstNode;
+ // Now do the sorting
+ for ( int i = n; i > 0; i-- ) {
+ insert->data = heap[1];
+ insert = insert->next;
+ if ( i > 1 ) {
+ heap[1] = heap[i];
+ heapSortPushDown( heap, 1, i - 1 );
+ }
+ }
+
+ delete [] realheap;
+}
+
+
+/*****************************************************************************
+ Q3GList stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+QDataStream &operator>>( QDataStream &s, Q3GList &list )
+{ // read list
+ return list.read( s );
+}
+
+QDataStream &operator<<( QDataStream &s, const Q3GList &list )
+{ // write list
+ return list.write( s );
+}
+
+/*!
+ Reads a list from the stream \a s.
+*/
+
+QDataStream &Q3GList::read( QDataStream &s )
+{
+ uint num;
+ s >> num; // read number of items
+ clear(); // clear list
+ while ( num-- ) { // read all items
+ Item d;
+ read( s, d );
+ Q_CHECK_PTR( d );
+ if ( !d ) // no memory
+ break;
+ Q3LNode *n = new Q3LNode( d );
+ Q_CHECK_PTR( n );
+ if ( !n ) // no memory
+ break;
+ n->next = 0;
+ if ( (n->prev = lastNode) ) // list is not empty
+ lastNode->next = n;
+ else // initialize list
+ firstNode = n;
+ lastNode = n;
+ numNodes++;
+ }
+ curNode = firstNode;
+ curIndex = curNode ? 0 : -1;
+ return s;
+}
+
+/*!
+ Writes the list to the stream \a s.
+*/
+
+QDataStream &Q3GList::write( QDataStream &s ) const
+{
+ s << count(); // write number of items
+ Q3LNode *n = firstNode;
+ while ( n ) { // write all items
+ write( s, n->data );
+ n = n->next;
+ }
+ return s;
+}
+
+#endif // QT_NO_DATASTREAM
+
+
+
+/*! \internal
+ */
+Q3LNode* Q3GList::erase( Q3LNode* it )
+{
+ Q3LNode* n = it;
+ it = it->next;
+ removeNode( n );
+ return it;
+}
+
+
+/*****************************************************************************
+ Q3GListIterator member functions
+ *****************************************************************************/
+
+/*!
+ \class Q3GListIterator
+ \reentrant
+ \brief The Q3GListIterator class is an internal class for implementing Q3PtrListIterator.
+
+ \internal
+
+ Q3GListIterator is a strictly internal class that does the heavy work for
+ Q3PtrListIterator.
+*/
+
+/*!
+ \internal
+ Constructs an iterator that operates on the list \a l.
+*/
+
+Q3GListIterator::Q3GListIterator( const Q3GList &l )
+{
+ list = (Q3GList *)&l; // get reference to list
+ curNode = list->firstNode; // set to first node
+ if ( !list->iterators ) {
+ list->iterators = new Q3GListIteratorList; // create iterator list
+ Q_CHECK_PTR( list->iterators );
+ }
+ list->iterators->add( this ); // attach iterator to list
+}
+
+/*!
+ \internal
+ Constructs a copy of the iterator \a it.
+*/
+
+Q3GListIterator::Q3GListIterator( const Q3GListIterator &it )
+{
+ list = it.list;
+ curNode = it.curNode;
+ if ( list )
+ list->iterators->add( this ); // attach iterator to list
+}
+
+/*!
+ \internal
+ Assigns a copy of the iterator \a it and returns a reference to this
+ iterator.
+*/
+
+Q3GListIterator &Q3GListIterator::operator=( const Q3GListIterator &it )
+{
+ if ( list ) // detach from old list
+ list->iterators->remove( this );
+ list = it.list;
+ curNode = it.curNode;
+ if ( list )
+ list->iterators->add( this ); // attach to new list
+ return *this;
+}
+
+/*!
+ \internal
+ Destroys the iterator.
+*/
+
+Q3GListIterator::~Q3GListIterator()
+{
+ if ( list ) // detach iterator from list
+ list->iterators->remove(this);
+}
+
+
+/*!
+ \fn bool Q3GListIterator::atFirst() const
+ \internal
+ Returns true if the iterator points to the first item, otherwise false.
+*/
+
+/*!
+ \fn bool Q3GListIterator::atLast() const
+ \internal
+ Returns true if the iterator points to the last item, otherwise false.
+*/
+
+
+/*!
+ \internal
+ Sets the list iterator to point to the first item in the list.
+*/
+
+Q3PtrCollection::Item Q3GListIterator::toFirst()
+{
+ if ( !list ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3GListIterator::toFirst: List has been deleted" );
+#endif
+ return 0;
+ }
+ return list->firstNode ? (curNode = list->firstNode)->getData() : 0;
+}
+
+/*!
+ \internal
+ Sets the list iterator to point to the last item in the list.
+*/
+
+Q3PtrCollection::Item Q3GListIterator::toLast()
+{
+ if ( !list ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3GListIterator::toLast: List has been deleted" );
+#endif
+ return 0;
+ }
+ return list->lastNode ? (curNode = list->lastNode)->getData() : 0;
+}
+
+
+/*!
+ \fn Q3PtrCollection::Item Q3GListIterator::get() const
+ \internal
+ Returns the iterator item.
+*/
+
+
+/*!
+ \internal
+ Moves to the next item (postfix).
+*/
+
+Q3PtrCollection::Item Q3GListIterator::operator()()
+{
+ if ( !curNode )
+ return 0;
+ Q3PtrCollection::Item d = curNode->getData();
+ curNode = curNode->next;
+ return d;
+}
+
+/*!
+ \internal
+ Moves to the next item (prefix).
+*/
+
+Q3PtrCollection::Item Q3GListIterator::operator++()
+{
+ if ( !curNode )
+ return 0;
+ curNode = curNode->next;
+ return curNode ? curNode->getData() : 0;
+}
+
+/*!
+ \internal
+ Moves \a jumps positions forward.
+*/
+
+Q3PtrCollection::Item Q3GListIterator::operator+=( uint jumps )
+{
+ while ( curNode && jumps-- )
+ curNode = curNode->next;
+ return curNode ? curNode->getData() : 0;
+}
+
+/*!
+ \internal
+ Moves to the previous item (prefix).
+*/
+
+Q3PtrCollection::Item Q3GListIterator::operator--()
+{
+ if ( !curNode )
+ return 0;
+ curNode = curNode->prev;
+ return curNode ? curNode->getData() : 0;
+}
+
+/*!
+ \internal
+ Moves \a jumps positions backward.
+*/
+
+Q3PtrCollection::Item Q3GListIterator::operator-=( uint jumps )
+{
+ while ( curNode && jumps-- )
+ curNode = curNode->prev;
+ return curNode ? curNode->getData() : 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3glist.h b/src/qt3support/tools/q3glist.h
new file mode 100644
index 0000000..5f907ec
--- /dev/null
+++ b/src/qt3support/tools/q3glist.h
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GLIST_H
+#define Q3GLIST_H
+
+#include <Qt3Support/q3ptrcollection.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3LNode
+{
+friend class Q3GList;
+friend class Q3GListIterator;
+friend class Q3GListStdIterator;
+public:
+ Q3PtrCollection::Item getData() { return data; }
+private:
+ Q3PtrCollection::Item data;
+ Q3LNode *prev;
+ Q3LNode *next;
+ Q3LNode( Q3PtrCollection::Item d ) { data = d; }
+};
+
+class Q3GListIteratorList; // internal helper class
+
+class Q_COMPAT_EXPORT Q3GList : public Q3PtrCollection // doubly linked generic list
+{
+friend class Q3GListIterator;
+friend class Q3GListIteratorList;
+friend class Q3GVector; // needed by Q3GVector::toList
+public:
+ uint count() const; // return number of nodes
+
+#ifndef QT_NO_DATASTREAM
+ QDataStream &read( QDataStream & ); // read list from stream
+ QDataStream &write( QDataStream & ) const; // write list to stream
+#endif
+protected:
+ Q3GList(); // create empty list
+ Q3GList( const Q3GList & ); // make copy of other list
+ virtual ~Q3GList();
+
+ Q3GList &operator=( const Q3GList & ); // assign from other list
+ bool operator==( const Q3GList& ) const;
+
+ void inSort( Q3PtrCollection::Item ); // add item sorted in list
+ void append( Q3PtrCollection::Item ); // add item at end of list
+ bool insertAt( uint index, Q3PtrCollection::Item ); // add item at i'th position
+ void relinkNode( Q3LNode * ); // relink as first item
+ bool removeNode( Q3LNode * ); // remove node
+ bool remove( Q3PtrCollection::Item = 0 ); // remove item (0=current)
+ bool removeRef( Q3PtrCollection::Item = 0 ); // remove item (0=current)
+ bool removeFirst(); // remove first item
+ bool removeLast(); // remove last item
+ bool removeAt( uint ); // remove item at i'th position
+ bool replaceAt( uint, Q3PtrCollection::Item ); // replace item at position i with item
+ Q3PtrCollection::Item takeNode( Q3LNode * ); // take out node
+ Q3PtrCollection::Item take(); // take out current item
+ Q3PtrCollection::Item takeAt( uint index ); // take out item at i'th pos
+ Q3PtrCollection::Item takeFirst(); // take out first item
+ Q3PtrCollection::Item takeLast(); // take out last item
+
+ void sort(); // sort all items;
+ void clear(); // remove all items
+
+ int findRef( Q3PtrCollection::Item, bool = true ); // find exact item in list
+ int find( Q3PtrCollection::Item, bool = true ); // find equal item in list
+
+ uint containsRef( Q3PtrCollection::Item ) const; // get number of exact matches
+ uint contains( Q3PtrCollection::Item ) const; // get number of equal matches
+
+ Q3PtrCollection::Item at( uint index ); // access item at i'th pos
+ int at() const; // get current index
+ Q3LNode *currentNode() const; // get current node
+
+ Q3PtrCollection::Item get() const; // get current item
+
+ Q3PtrCollection::Item cfirst() const; // get ptr to first list item
+ Q3PtrCollection::Item clast() const; // get ptr to last list item
+ Q3PtrCollection::Item first(); // set first item in list curr
+ Q3PtrCollection::Item last(); // set last item in list curr
+ Q3PtrCollection::Item next(); // set next item in list curr
+ Q3PtrCollection::Item prev(); // set prev item in list curr
+
+ void toVector( Q3GVector * ) const; // put items in vector
+
+ virtual int compareItems( Q3PtrCollection::Item, Q3PtrCollection::Item );
+
+#ifndef QT_NO_DATASTREAM
+ virtual QDataStream &read( QDataStream &, Q3PtrCollection::Item & );
+ virtual QDataStream &write( QDataStream &, Q3PtrCollection::Item ) const;
+#endif
+
+ Q3LNode* begin() const { return firstNode; }
+ Q3LNode* end() const { return 0; }
+ Q3LNode* erase( Q3LNode* it );
+
+private:
+ void prepend( Q3PtrCollection::Item ); // add item at start of list
+
+ void heapSortPushDown( Q3PtrCollection::Item* heap, int first, int last );
+
+ Q3LNode *firstNode; // first node
+ Q3LNode *lastNode; // last node
+ Q3LNode *curNode; // current node
+ int curIndex; // current index
+ uint numNodes; // number of nodes
+ Q3GListIteratorList *iterators; // list of iterators
+
+ Q3LNode *locate( uint ); // get node at i'th pos
+ Q3LNode *unlink(); // unlink node
+};
+
+
+inline uint Q3GList::count() const
+{
+ return numNodes;
+}
+
+inline bool Q3GList::removeFirst()
+{
+ first();
+ return remove();
+}
+
+inline bool Q3GList::removeLast()
+{
+ last();
+ return remove();
+}
+
+inline int Q3GList::at() const
+{
+ return curIndex;
+}
+
+inline Q3PtrCollection::Item Q3GList::at( uint index )
+{
+ Q3LNode *n = locate( index );
+ return n ? n->data : 0;
+}
+
+inline Q3LNode *Q3GList::currentNode() const
+{
+ return curNode;
+}
+
+inline Q3PtrCollection::Item Q3GList::get() const
+{
+ return curNode ? curNode->data : 0;
+}
+
+inline Q3PtrCollection::Item Q3GList::cfirst() const
+{
+ return firstNode ? firstNode->data : 0;
+}
+
+inline Q3PtrCollection::Item Q3GList::clast() const
+{
+ return lastNode ? lastNode->data : 0;
+}
+
+
+/*****************************************************************************
+ Q3GList stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_COMPAT_EXPORT QDataStream &operator>>( QDataStream &, Q3GList & );
+Q_COMPAT_EXPORT QDataStream &operator<<( QDataStream &, const Q3GList & );
+#endif
+
+/*****************************************************************************
+ Q3GListIterator class
+ *****************************************************************************/
+
+class Q_COMPAT_EXPORT Q3GListIterator // Q3GList iterator
+{
+friend class Q3GList;
+friend class Q3GListIteratorList;
+protected:
+ Q3GListIterator( const Q3GList & );
+ Q3GListIterator( const Q3GListIterator & );
+ Q3GListIterator &operator=( const Q3GListIterator & );
+ ~Q3GListIterator();
+
+ bool atFirst() const; // test if at first item
+ bool atLast() const; // test if at last item
+ Q3PtrCollection::Item toFirst(); // move to first item
+ Q3PtrCollection::Item toLast(); // move to last item
+
+ Q3PtrCollection::Item get() const; // get current item
+ Q3PtrCollection::Item operator()(); // get current and move to next
+ Q3PtrCollection::Item operator++(); // move to next item (prefix)
+ Q3PtrCollection::Item operator+=(uint); // move n positions forward
+ Q3PtrCollection::Item operator--(); // move to prev item (prefix)
+ Q3PtrCollection::Item operator-=(uint); // move n positions backward
+
+protected:
+ Q3GList *list; // reference to list
+
+private:
+ Q3LNode *curNode; // current node in list
+};
+
+
+inline bool Q3GListIterator::atFirst() const
+{
+ return curNode == list->firstNode;
+}
+
+inline bool Q3GListIterator::atLast() const
+{
+ return curNode == list->lastNode;
+}
+
+inline Q3PtrCollection::Item Q3GListIterator::get() const
+{
+ return curNode ? curNode->data : 0;
+}
+
+class Q_COMPAT_EXPORT Q3GListStdIterator
+{
+public:
+ inline Q3GListStdIterator( Q3LNode* n ) : node( n ){}
+ inline operator Q3LNode* () { return node; }
+protected:
+ inline Q3LNode *next() { return node->next; }
+ Q3LNode *node;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GLIST_H
diff --git a/src/qt3support/tools/q3gvector.cpp b/src/qt3support/tools/q3gvector.cpp
new file mode 100644
index 0000000..6736cd4
--- /dev/null
+++ b/src/qt3support/tools/q3gvector.cpp
@@ -0,0 +1,597 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#if defined(Q_CC_BOR)
+// needed for qsort() because of a std namespace problem on Borland
+#include "qplatformdefs.h"
+#endif
+
+#define Q3GVECTOR_CPP
+#include "q3gvector.h"
+#include "q3glist.h"
+#include "qstring.h"
+#include "qdatastream.h"
+#include <stdlib.h>
+
+#ifndef QT_NO_THREAD
+# include "private/qmutexpool_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define USE_MALLOC // comment to use new/delete
+
+#undef NEW
+#undef DELETE
+
+#if defined(USE_MALLOC)
+#define NEW(type,size) ((type*)malloc(size*sizeof(type)))
+#define DELETE(array) (free((char*)array))
+#else
+#define NEW(type,size) (new type[size])
+#define DELETE(array) (delete[] array)
+#define DONT_USE_REALLOC // comment to use realloc()
+#endif
+
+/*!
+ \class Q3GVector
+ \reentrant
+
+ \brief The Q3GVector class is an internal class for implementing Qt
+ collection classes.
+
+ \internal
+
+ Q3GVector is an internal class that acts as a base class for the
+ Q3PtrVector collection class.
+
+ Q3GVector has some virtual functions that may be reimplemented in
+ subclasses to customize behavior.
+
+ \list
+ \i compareItems() compares two collection/vector items.
+ \i read() reads a collection/vector item from a QDataStream.
+ \i write() writes a collection/vector item to a QDataStream.
+ \endlist
+*/
+
+/*****************************************************************************
+ Default implementation of virtual functions
+ *****************************************************************************/
+
+/*!
+ This virtual function compares two list items.
+
+ Returns:
+ <ul>
+ <li> 0 if \a d1 == \a d2
+ <li> non-zero if \a d1 != \a d2
+ </ul>
+
+ This function returns \e int rather than \e bool so that
+ reimplementations can return one of three values and use it to sort
+ by:
+ <ul>
+ <li> 0 if \a d1 == \a d2
+ <li> \> 0 (positive integer) if \a d1 \> \a d2
+ <li> \< 0 (negative integer) if \a d1 \< \a d2
+ </ul>
+
+ The Q3PtrVector::sort() and Q3PtrVector::bsearch() functions require that
+ compareItems() is implemented as described here.
+
+ This function should not modify the vector because some const
+ functions call compareItems().
+*/
+
+int Q3GVector::compareItems( Item d1, Item d2 )
+{
+ return d1 != d2; // compare pointers
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ Reads a collection/vector item from the stream \a s and returns a reference
+ to the stream.
+
+ The default implementation sets \a d to 0.
+
+ \sa write()
+*/
+
+QDataStream &Q3GVector::read( QDataStream &s, Item &d )
+{ // read item from stream
+ d = 0;
+ return s;
+}
+
+/*!
+ Writes a collection/vector item to the stream \a s and returns a reference
+ to the stream.
+
+ The default implementation does nothing.
+
+ \sa read()
+*/
+
+QDataStream &Q3GVector::write( QDataStream &s, Item ) const
+{ // write item to stream
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+/*****************************************************************************
+ Q3GVector member functions
+ *****************************************************************************/
+
+Q3GVector::Q3GVector() // create empty vector
+{
+ vec = 0;
+ len = numItems = 0;
+}
+
+Q3GVector::Q3GVector( uint size ) // create vectors with nullptrs
+{
+ len = size;
+ numItems = 0;
+ if ( len == 0 ) { // zero length
+ vec = 0;
+ return;
+ }
+ vec = NEW(Item,len);
+ Q_CHECK_PTR( vec );
+ memset( (void*)vec, 0, len*sizeof(Item) ); // fill with nulls
+}
+
+Q3GVector::Q3GVector( const Q3GVector &a ) // make copy of other vector
+ : Q3PtrCollection( a )
+{
+ len = a.len;
+ numItems = a.numItems;
+ if ( len == 0 ) {
+ vec = 0;
+ return;
+ }
+ vec = NEW( Item, len );
+ Q_CHECK_PTR( vec );
+ for ( uint i = 0; i < len; i++ ) {
+ if ( a.vec[i] ) {
+ vec[i] = newItem( a.vec[i] );
+ Q_CHECK_PTR( vec[i] );
+ } else {
+ vec[i] = 0;
+ }
+ }
+}
+
+Q3GVector::~Q3GVector()
+{
+ clear();
+}
+
+Q3GVector& Q3GVector::operator=( const Q3GVector &v )
+{
+ if ( &v == this )
+ return *this;
+
+ clear();
+ len = v.len;
+ numItems = v.numItems;
+ if ( len == 0 ) {
+ vec = 0;
+ return *this;
+ }
+ vec = NEW( Item, len );
+ Q_CHECK_PTR( vec );
+ for ( uint i = 0; i < len; i++ ) {
+ if ( v.vec[i] ) {
+ vec[i] = newItem( v.vec[i] );
+ Q_CHECK_PTR( vec[i] );
+ } else {
+ vec[i] = 0;
+ }
+ }
+ return *this;
+}
+
+
+bool Q3GVector::insert( uint index, Item d ) // insert item at index
+{
+#if defined(QT_CHECK_RANGE)
+ if ( index >= len ) { // range error
+ qWarning( "Q3GVector::insert: Index %d out of range", index );
+ return false;
+ }
+#endif
+ if ( vec[index] ) { // remove old item
+ deleteItem( vec[index] );
+ numItems--;
+ }
+ if ( d ) {
+ vec[index] = newItem( d );
+ Q_CHECK_PTR( vec[index] );
+ numItems++;
+ return vec[index] != 0;
+ } else {
+ vec[index] = 0; // reset item
+ }
+ return true;
+}
+
+bool Q3GVector::remove( uint index ) // remove item at index
+{
+#if defined(QT_CHECK_RANGE)
+ if ( index >= len ) { // range error
+ qWarning( "Q3GVector::remove: Index %d out of range", index );
+ return false;
+ }
+#endif
+ if ( vec[index] ) { // valid item
+ deleteItem( vec[index] ); // delete it
+ vec[index] = 0; // reset pointer
+ numItems--;
+ }
+ return true;
+}
+
+Q3PtrCollection::Item Q3GVector::take( uint index ) // take out item
+{
+#if defined(QT_CHECK_RANGE)
+ if ( index >= len ) { // range error
+ qWarning( "Q3GVector::take: Index %d out of range", index );
+ return 0;
+ }
+#endif
+ Item d = vec[index]; // don't delete item
+ if ( d )
+ numItems--;
+ vec[index] = 0;
+ return d;
+}
+
+void Q3GVector::clear() // clear vector
+{
+ if ( vec ) {
+ for ( uint i=0; i<len; i++ ) { // delete each item
+ if ( vec[i] )
+ deleteItem( vec[i] );
+ }
+ DELETE(vec);
+ vec = 0;
+ len = numItems = 0;
+ }
+}
+
+bool Q3GVector::resize( uint newsize ) // resize array
+{
+ if ( newsize == len ) // nothing to do
+ return true;
+ if ( vec ) { // existing data
+ if ( newsize < len ) { // shrink vector
+ uint i = newsize;
+ while ( i < len ) { // delete lost items
+ if ( vec[i] ) {
+ deleteItem( vec[i] );
+ numItems--;
+ }
+ i++;
+ }
+ }
+ if ( newsize == 0 ) { // vector becomes empty
+ DELETE(vec);
+ vec = 0;
+ len = numItems = 0;
+ return true;
+ }
+#if defined(DONT_USE_REALLOC)
+ if ( newsize == 0 ) {
+ DELETE(vec);
+ vec = 0;
+ return false;
+ }
+ Item *newvec = NEW(Item,newsize); // manual realloc
+ memcpy( newvec, vec, (len < newsize ? len : newsize)*sizeof(Item) );
+ DELETE(vec);
+ vec = newvec;
+#else
+ vec = (Item*)realloc( (char *)vec, newsize*sizeof(Item) );
+#endif
+ } else { // create new vector
+ vec = NEW(Item,newsize);
+ len = numItems = 0;
+ }
+ Q_CHECK_PTR( vec );
+ if ( !vec ) // no memory
+ return false;
+ if ( newsize > len ) // init extra space added
+ memset( (void*)&vec[len], 0, (newsize-len)*sizeof(Item) );
+ len = newsize;
+ return true;
+}
+
+
+bool Q3GVector::fill( Item d, int flen ) // resize and fill vector
+{
+ if ( flen < 0 )
+ flen = len; // default: use vector length
+ else if ( !resize( flen ) )
+ return false;
+ for ( uint i=0; i<(uint)flen; i++ ) // insert d at every index
+ insert( i, d );
+ return true;
+}
+
+
+static Q3GVector *sort_vec=0; // current sort vector
+
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int _cdecl cmp_vec( const void *n1, const void *n2 )
+#else
+static int cmp_vec( const void *n1, const void *n2 )
+#endif
+{
+ return sort_vec->compareItems( *((Q3PtrCollection::Item*)n1), *((Q3PtrCollection::Item*)n2) );
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+
+void Q3GVector::sort() // sort vector
+{
+ if ( count() == 0 ) // no elements
+ return;
+ register Item *start = &vec[0];
+ register Item *end = &vec[len-1];
+ Item tmp;
+ for (;;) { // put all zero elements behind
+ while ( start < end && *start != 0 )
+ start++;
+ while ( end > start && *end == 0 )
+ end--;
+ if ( start < end ) {
+ tmp = *start;
+ *start = *end;
+ *end = tmp;
+ } else {
+ break;
+ }
+ }
+
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&sort_vec));
+#endif
+
+ sort_vec = (Q3GVector*)this;
+ qsort( vec, count(), sizeof(Item), cmp_vec );
+ sort_vec = 0;
+}
+
+int Q3GVector::bsearch( Item d ) const // binary search; when sorted
+{
+ if ( !len )
+ return -1;
+ if ( !d ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3GVector::bsearch: Cannot search for null object" );
+#endif
+ return -1;
+ }
+ int n1 = 0;
+ int n2 = len - 1;
+ int mid = 0;
+ bool found = false;
+ while ( n1 <= n2 ) {
+ int res;
+ mid = (n1 + n2)/2;
+ if ( vec[mid] == 0 ) // null item greater
+ res = -1;
+ else
+ res = ((Q3GVector*)this)->compareItems( d, vec[mid] );
+ if ( res < 0 )
+ n2 = mid - 1;
+ else if ( res > 0 )
+ n1 = mid + 1;
+ else { // found it
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ return -1;
+ // search to first of equal items
+ while ( (mid - 1 >= 0) && !((Q3GVector*)this)->compareItems(d, vec[mid-1]) )
+ mid--;
+ return mid;
+}
+
+int Q3GVector::findRef( Item d, uint index) const // find exact item in vector
+{
+#if defined(QT_CHECK_RANGE)
+ if ( index > len ) { // range error
+ qWarning( "Q3GVector::findRef: Index %d out of range", index );
+ return -1;
+ }
+#endif
+ for ( uint i=index; i<len; i++ ) {
+ if ( vec[i] == d )
+ return i;
+ }
+ return -1;
+}
+
+int Q3GVector::find( Item d, uint index ) const // find equal item in vector
+{
+#if defined(QT_CHECK_RANGE)
+ if ( index >= len ) { // range error
+ qWarning( "Q3GVector::find: Index %d out of range", index );
+ return -1;
+ }
+#endif
+ for ( uint i=index; i<len; i++ ) {
+ if ( vec[i] == 0 && d == 0 ) // found null item
+ return i;
+ if ( vec[i] && ((Q3GVector*)this)->compareItems( vec[i], d ) == 0 )
+ return i;
+ }
+ return -1;
+}
+
+uint Q3GVector::containsRef( Item d ) const // get number of exact matches
+{
+ uint count = 0;
+ for ( uint i=0; i<len; i++ ) {
+ if ( vec[i] == d )
+ count++;
+ }
+ return count;
+}
+
+uint Q3GVector::contains( Item d ) const // get number of equal matches
+{
+ uint count = 0;
+ for ( uint i=0; i<len; i++ ) {
+ if ( vec[i] == 0 && d == 0 ) // count null items
+ count++;
+ if ( vec[i] && ((Q3GVector*)this)->compareItems( vec[i], d ) == 0 )
+ count++;
+ }
+ return count;
+}
+
+bool Q3GVector::insertExpand( uint index, Item d )// insert and grow if necessary
+{
+ if ( index >= len ) {
+ if ( !resize( index+1 ) ) // no memory
+ return false;
+ }
+ insert( index, d );
+ return true;
+}
+
+void Q3GVector::toList( Q3GList *list ) const // store items in list
+{
+ list->clear();
+ for ( uint i=0; i<len; i++ ) {
+ if ( vec[i] )
+ list->append( vec[i] );
+ }
+}
+
+
+void Q3GVector::warningIndexRange( uint i )
+{
+#if defined(QT_CHECK_RANGE)
+ qWarning( "Q3GVector::operator[]: Index %d out of range", i );
+#else
+ Q_UNUSED( i )
+#endif
+}
+
+
+/*****************************************************************************
+ Q3GVector stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+QDataStream &operator>>( QDataStream &s, Q3GVector &vec )
+{ // read vector
+ return vec.read( s );
+}
+
+QDataStream &operator<<( QDataStream &s, const Q3GVector &vec )
+{ // write vector
+ return vec.write( s );
+}
+
+QDataStream &Q3GVector::read( QDataStream &s ) // read vector from stream
+{
+ uint num;
+ s >> num; // read number of items
+ clear(); // clear vector
+ resize( num );
+ for (uint i=0; i<num; i++) { // read all items
+ Item d;
+ read( s, d );
+ Q_CHECK_PTR( d );
+ if ( !d ) // no memory
+ break;
+ vec[i] = d;
+ }
+ return s;
+}
+
+QDataStream &Q3GVector::write( QDataStream &s ) const
+{ // write vector to stream
+ uint num = count();
+ s << num; // number of items to write
+ num = size();
+ for (uint i=0; i<num; i++) { // write non-null items
+ if ( vec[i] )
+ write( s, vec[i] );
+ }
+ return s;
+}
+
+/* Returns whether v equals this vector or not */
+
+bool Q3GVector::operator==( const Q3GVector &v ) const
+{
+ if ( size() != v.size() )
+ return false;
+ if ( count() != v.count() )
+ return false;
+ for ( int i = 0; i < (int)size(); ++i ) {
+ if ( ( (Q3GVector*)this )->compareItems( at( i ), v.at( i ) ) != 0 )
+ return false;
+ }
+ return true;
+}
+
+#endif // QT_NO_DATASTREAM
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3gvector.h b/src/qt3support/tools/q3gvector.h
new file mode 100644
index 0000000..9d27dfc
--- /dev/null
+++ b/src/qt3support/tools/q3gvector.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GVECTOR_H
+#define Q3GVECTOR_H
+
+#include <Qt3Support/q3ptrcollection.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3GVector : public Q3PtrCollection // generic vector
+{
+friend class Q3GList; // needed by Q3GList::toVector
+public:
+#ifndef QT_NO_DATASTREAM
+ QDataStream &read( QDataStream & ); // read vector from stream
+ QDataStream &write( QDataStream & ) const; // write vector to stream
+#endif
+ virtual int compareItems( Item, Item );
+
+protected:
+ Q3GVector(); // create empty vector
+ Q3GVector( uint size ); // create vector with nullptrs
+ Q3GVector( const Q3GVector &v ); // make copy of other vector
+ ~Q3GVector();
+
+ Q3GVector &operator=( const Q3GVector &v ); // assign from other vector
+ bool operator==( const Q3GVector &v ) const;
+
+ Item *data() const { return vec; }
+ uint size() const { return len; }
+ uint count() const { return numItems; }
+
+ bool insert( uint index, Item ); // insert item at index
+ bool remove( uint index ); // remove item
+ Item take( uint index ); // take out item
+
+ void clear(); // clear vector
+ bool resize( uint newsize ); // resize vector
+
+ bool fill( Item, int flen ); // resize and fill vector
+
+ void sort(); // sort vector
+ int bsearch( Item ) const; // binary search (when sorted)
+
+ int findRef( Item, uint index ) const; // find exact item in vector
+ int find( Item, uint index ) const; // find equal item in vector
+ uint containsRef( Item ) const; // get number of exact matches
+ uint contains( Item ) const; // get number of equal matches
+
+ Item at( uint index ) const // return indexed item
+ {
+#if defined(QT_CHECK_RANGE)
+ if ( index >= len )
+ warningIndexRange( index );
+#endif
+ return vec[index];
+ }
+
+ bool insertExpand( uint index, Item ); // insert, expand if necessary
+
+ void toList( Q3GList * ) const; // put items in list
+
+#ifndef QT_NO_DATASTREAM
+ virtual QDataStream &read( QDataStream &, Item & );
+ virtual QDataStream &write( QDataStream &, Item ) const;
+#endif
+private:
+ Item *vec;
+ uint len;
+ uint numItems;
+
+ static void warningIndexRange( uint );
+};
+
+
+/*****************************************************************************
+ Q3GVector stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_COMPAT_EXPORT QDataStream &operator>>( QDataStream &, Q3GVector & );
+Q_COMPAT_EXPORT QDataStream &operator<<( QDataStream &, const Q3GVector & );
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GVECTOR_H
diff --git a/src/qt3support/tools/q3intcache.h b/src/qt3support/tools/q3intcache.h
new file mode 100644
index 0000000..2198901
--- /dev/null
+++ b/src/qt3support/tools/q3intcache.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3INTCACHE_H
+#define Q3INTCACHE_H
+
+#include <Qt3Support/q3gcache.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3IntCache
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GCache
+#endif
+{
+public:
+ Q3IntCache( const Q3IntCache<type> &c ) : Q3GCache(c) {}
+ Q3IntCache( int maxCost=100, int size=17 )
+ : Q3GCache( maxCost, size, IntKey, false, false ) {}
+ ~Q3IntCache() { clear(); }
+ Q3IntCache<type> &operator=( const Q3IntCache<type> &c )
+ { return (Q3IntCache<type>&)Q3GCache::operator=(c); }
+ int maxCost() const { return Q3GCache::maxCost(); }
+ int totalCost() const { return Q3GCache::totalCost(); }
+ void setMaxCost( int m) { Q3GCache::setMaxCost(m); }
+ uint count() const { return Q3GCache::count(); }
+ uint size() const { return Q3GCache::size(); }
+ bool isEmpty() const { return Q3GCache::count() == 0; }
+ bool insert( long k, const type *d, int c=1, int p=0 )
+ { return Q3GCache::insert_other((const char*)k,(Item)d,c,p); }
+ bool remove( long k )
+ { return Q3GCache::remove_other((const char*)k); }
+ type *take( long k )
+ { return (type *)Q3GCache::take_other((const char*)k);}
+ void clear() { Q3GCache::clear(); }
+ type *find( long k, bool ref=true ) const
+ { return (type *)Q3GCache::find_other( (const char*)k,ref);}
+ type *operator[]( long k ) const
+ { return (type *)Q3GCache::find_other( (const char*)k); }
+ void statistics() const { Q3GCache::statistics(); }
+private:
+ void deleteItem( Item d );
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3IntCache<void>::deleteItem( Q3PtrCollection::Item )
+{
+}
+#endif
+
+template<class type> inline void Q3IntCache<type>::deleteItem( Q3PtrCollection::Item d )
+{
+ if ( del_item ) delete (type *)d;
+}
+
+template<class type>
+class Q3IntCacheIterator : public Q3GCacheIterator
+{
+public:
+ Q3IntCacheIterator( const Q3IntCache<type> &c )
+ : Q3GCacheIterator( (Q3GCache &)c ) {}
+ Q3IntCacheIterator( const Q3IntCacheIterator<type> &ci )
+ : Q3GCacheIterator((Q3GCacheIterator &)ci) {}
+ Q3IntCacheIterator<type> &operator=( const Q3IntCacheIterator<type>&ci )
+ { return ( Q3IntCacheIterator<type>&)Q3GCacheIterator::operator=( ci );}
+ uint count() const { return Q3GCacheIterator::count(); }
+ bool isEmpty() const { return Q3GCacheIterator::count() == 0; }
+ bool atFirst() const { return Q3GCacheIterator::atFirst(); }
+ bool atLast() const { return Q3GCacheIterator::atLast(); }
+ type *toFirst() { return (type *)Q3GCacheIterator::toFirst(); }
+ type *toLast() { return (type *)Q3GCacheIterator::toLast(); }
+ operator type *() const { return (type *)Q3GCacheIterator::get(); }
+ type *current() const { return (type *)Q3GCacheIterator::get(); }
+ long currentKey() const { return (long)Q3GCacheIterator::getKeyInt();}
+ type *operator()() { return (type *)Q3GCacheIterator::operator()();}
+ type *operator++() { return (type *)Q3GCacheIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GCacheIterator::operator+=(j);}
+ type *operator--() { return (type *)Q3GCacheIterator::operator--(); }
+ type *operator-=(uint j) { return (type *)Q3GCacheIterator::operator-=(j);}
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3INTCACHE_H
diff --git a/src/qt3support/tools/q3intcache.qdoc b/src/qt3support/tools/q3intcache.qdoc
new file mode 100644
index 0000000..02af31c
--- /dev/null
+++ b/src/qt3support/tools/q3intcache.qdoc
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3IntCache
+ \brief The Q3IntCache class is a template class that provides a cache based on long keys.
+ \compat
+
+ Q3IntCache is implemented as a template class. Define a template
+ instance Q3IntCache\<X\> to create a cache that operates on
+ pointers to X, or X*.
+
+ A cache is a least recently used (LRU) list of cache items,
+ accessed via \c long keys. Each cache item has a cost. The sum
+ of item costs, totalCost(), will not exceed the maximum cache
+ cost, maxCost(). If inserting a new item would cause the total
+ cost to exceed the maximum cost, the least recently used items in
+ the cache are removed.
+
+ Apart from insert(), by far the most important function is find()
+ (which also exists as operator[]). This function looks up an
+ item, returns it, and by default marks it as being the most
+ recently used item.
+
+ There are also methods to remove() or take() an object from the
+ cache. Calling setAutoDelete(TRUE) for a cache tells it to delete
+ items that are removed. The default is to not delete items when
+ they are removed (i.e. remove() and take() are equivalent).
+
+ When inserting an item into the cache, only the pointer is copied,
+ not the item itself. This is called a shallow copy. It is possible
+ to make the cache copy all of the item's data (known as a deep
+ copy) when an item is inserted. insert() calls the virtual
+ function Q3PtrCollection::newItem() for the item to be inserted.
+ Inherit a dictionary and reimplement newItem() if you want deep
+ copies.
+
+ When removing a cache item, the item will be automatically
+ deleted if auto-deletion is enabled.
+
+ There is a Q3IntCacheIterator which may be used to traverse the
+ items in the cache in arbitrary order.
+
+ \sa Q3IntCacheIterator, Q3Cache, Q3AsciiCache
+*/
+
+/*!
+ \fn Q3IntCache::Q3IntCache( const Q3IntCache<type> &c )
+
+ \internal
+
+ Do not use. A Q3IntCache cannot be copied. Calls qFatal() in debug version.
+*/
+
+/*!
+ \fn Q3IntCache::Q3IntCache( int maxCost, int size )
+
+ Constructs a cache whose contents will never have a total cost
+ greater than \a maxCost and which is expected to contain less than
+ \a size items.
+
+ \a size is actually the size of an internal hash array; it's
+ usually best to make it prime and at least 50% bigger than the
+ largest expected number of items in the cache.
+
+ Each inserted item is associated with a cost. When inserting a new
+ item, if the total cost of all items in the cache will exceed \a
+ maxCost, the cache will start throwing out the older (least
+ recently used) items until there is enough room for the new item
+ to be inserted.
+*/
+
+/*!
+ \fn Q3IntCache::~Q3IntCache()
+
+ Removes all items from the cache and then destroys the int cache.
+ If auto-deletion is enabled the cache's items are deleted. All
+ iterators that access this cache will be reset.
+*/
+
+/*!
+ \fn Q3IntCache<type>& Q3IntCache::operator=( const Q3IntCache<type> &c )
+
+ \internal
+
+ Do not use. A Q3IntCache cannot be copied. Calls qFatal() in debug version.
+*/
+
+/*!
+ \fn int Q3IntCache::maxCost() const
+
+ Returns the maximum allowed total cost of the cache.
+
+ \sa setMaxCost() totalCost()
+*/
+
+/*!
+ \fn int Q3IntCache::totalCost() const
+
+ Returns the total cost of the items in the cache. This is an
+ integer in the range 0 to maxCost().
+
+ \sa setMaxCost()
+*/
+
+/*!
+ \fn void Q3IntCache::setMaxCost( int m )
+
+ Sets the maximum allowed total cost of the cache to \a m. If the
+ current total cost is greater than \a m, some items are removed
+ immediately.
+
+ \sa maxCost() totalCost()
+*/
+
+/*!
+ \fn uint Q3IntCache::count() const
+
+ Returns the number of items in the cache.
+
+ \sa totalCost()
+*/
+
+/*!
+ \fn uint Q3IntCache::size() const
+
+ Returns the size of the hash array used to implement the cache.
+ This should be a bit larger than count() is likely to be.
+*/
+
+/*!
+ \fn bool Q3IntCache::isEmpty() const
+
+ Returns TRUE if the cache is empty; otherwise returns FALSE.
+*/
+
+/*!
+ \fn bool Q3IntCache::insert( long k, const type *d, int c, int p )
+
+ Inserts the item \a d into the cache with key \a k and assigns it
+ a cost of \a c (default 1). Returns TRUE if it succeeds; otherwise
+ returns FALSE.
+
+ The cache's size is limited, and if the total cost is too high,
+ Q3IntCache will remove old, least-used items until there is room
+ for this new item.
+
+ The parameter \a p is internal and should be left at the default
+ value (0).
+
+ \warning If this function returns FALSE (for example, the cost \c,
+ exceeds maxCost()), you must delete \a d yourself. Additionally,
+ be very careful about using \a d after calling this function. Any
+ other insertions into the cache, from anywhere in the application
+ or within Qt itself, could cause the object to be discarded from
+ the cache and the pointer to become invalid.
+*/
+
+/*!
+ \fn bool Q3IntCache::remove( long k )
+
+ Removes the item associated with \a k, and returns TRUE if the
+ item was present in the cache; otherwise returns FALSE.
+
+ The item is deleted if auto-deletion has been enabled, i.e. if you
+ have called setAutoDelete(TRUE).
+
+ If there are two or more items with equal keys, the one that was
+ inserted most recently is removed.
+
+ All iterators that refer to the removed item are set to point to
+ the next item in the cache's traversal order.
+
+ \sa take(), clear()
+*/
+
+/*!
+ \fn type * Q3IntCache::take( long k )
+
+ Takes the item associated with \a k out of the cache without
+ deleting it, and returns a pointer to the item taken out or 0 if
+ the key does not exist in the cache.
+
+ If there are two or more items with equal keys, the one that was
+ inserted most recently is taken.
+
+ All iterators that refer to the taken item are set to point to the
+ next item in the cache's traversal order.
+
+ \sa remove(), clear()
+*/
+
+/*!
+ \fn void Q3IntCache::clear()
+
+ Removes all items from the cache, and deletes them if
+ auto-deletion has been enabled.
+
+ All cache iterators that operate this on cache are reset.
+
+ \sa remove() take()
+*/
+
+/*!
+ \fn type * Q3IntCache::find( long k, bool ref ) const
+
+ Returns the item associated with \a k, or 0 if the key does not
+ exist in the cache. If \a ref is TRUE (the default), the item is
+ moved to the front of the least recently used list.
+
+ If there are two or more items with equal keys, the one that was
+ inserted most recently is returned.
+*/
+
+/*!
+ \fn type * Q3IntCache::operator[]( long k ) const
+
+ Returns the item associated with \a k, or 0 if \a k does not exist
+ in the cache, and moves the item to the front of the least
+ recently used list.
+
+ If there are two or more items with equal keys, the one that was
+ inserted most recently is returned.
+
+ This is the same as find( k, TRUE ).
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3IntCache::statistics() const
+
+ A debug-only utility function. Prints out cache usage, hit/miss,
+ and distribution information using qDebug(). This function does
+ nothing in the release library.
+*/
+
+/*!
+ \class Q3IntCacheIterator
+ \brief The Q3IntCacheIterator class provides an iterator for Q3IntCache collections.
+ \compat
+
+ Note that the traversal order is arbitrary; you are not guaranteed
+ any particular order. If new objects are inserted into the cache
+ while the iterator is active, the iterator may or may not see
+ them.
+
+ Multiple iterators are completely independent, even when they
+ operate on the same Q3IntCache. Q3IntCache updates all iterators
+ that refer an item when that item is removed.
+
+ Q3IntCacheIterator provides an operator++(), and an operator+=() to
+ traverse the cache; current() and currentKey() to access the
+ current cache item and its key; atFirst() atLast(), which return
+ TRUE if the iterator points to the first/last item in the cache;
+ isEmpty(), which returns TRUE if the cache is empty; and count(),
+ which returns the number of items in the cache.
+
+ Note that atFirst() and atLast() refer to the iterator's arbitrary
+ ordering, not to the cache's internal least recently used list.
+
+ \sa Q3IntCache
+*/
+
+/*!
+ \fn Q3IntCacheIterator::Q3IntCacheIterator( const Q3IntCache<type> &cache )
+
+ Constructs an iterator for \a cache. The current iterator item is
+ set to point to the first item in the \a cache (or rather, the
+ first item is defined to be the item at which this constructor
+ sets the iterator to point).
+*/
+
+/*!
+ \fn Q3IntCacheIterator::Q3IntCacheIterator (const Q3IntCacheIterator<type> & ci)
+
+ Constructs an iterator for the same cache as \a ci. The new
+ iterator starts at the same item as ci.current(), but moves
+ independently from there on.
+*/
+
+/*!
+ \fn Q3IntCacheIterator<type>& Q3IntCacheIterator::operator=( const Q3IntCacheIterator<type> &ci )
+
+ Makes this an iterator for the same cache as \a ci. The new
+ iterator starts at the same item as ci.current(), but moves
+ independently thereafter.
+*/
+
+/*!
+ \fn uint Q3IntCacheIterator::count() const
+
+ Returns the number of items in the cache on which this iterator
+ operates.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3IntCacheIterator::isEmpty() const
+
+ Returns TRUE if the cache is empty; otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn bool Q3IntCacheIterator::atFirst() const
+
+ Returns TRUE if the iterator points to the first item in the
+ cache; otherwise returns FALSE. Note that this refers to the
+ iterator's arbitrary ordering, not to the cache's internal least
+ recently used list.
+
+ \sa toFirst(), atLast()
+*/
+
+/*!
+ \fn bool Q3IntCacheIterator::atLast() const
+
+ Returns TRUE if the iterator points to the last item in the cache;
+ otherwise returns FALSE. Note that this refers to the iterator's
+ arbitrary ordering, not to the cache's internal least recently
+ used list.
+
+ \sa toLast(), atFirst()
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::toFirst()
+
+ Sets the iterator to point to the first item in the cache and
+ returns a pointer to the item.
+
+ Sets the iterator to 0, and returns 0, if the cache is empty.
+
+ \sa toLast() isEmpty()
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::toLast()
+
+ Sets the iterator to point to the last item in the cache and
+ returns a pointer to the item.
+
+ Sets the iterator to 0, and returns 0, if the cache is empty.
+
+ \sa toFirst() isEmpty()
+*/
+
+/*!
+ \fn Q3IntCacheIterator::operator type *() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::current() const
+
+ Returns a pointer to the current iterator item.
+*/
+
+/*!
+ \fn long Q3IntCacheIterator::currentKey() const
+
+ Returns the key for the current iterator item.
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::operator()()
+
+ Makes the succeeding item current and returns the original current
+ item.
+
+ If the current iterator item was the last item in the cache or if
+ it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::operator+=( uint jump )
+
+ Returns the item \a jump positions after the current item, or 0 if
+ it is beyond the last item. Makes this the current item.
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::operator-=( uint jump )
+
+ Returns the item \a jump positions before the current item, or 0
+ if it is beyond the first item. Makes this the current item.
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::operator++()
+
+ Prefix ++ makes the iterator point to the item just after
+ current(), and makes it the new current item for the iterator. If
+ current() was the last item, operator--() returns 0.
+*/
+
+/*!
+ \fn type *Q3IntCacheIterator::operator--()
+
+ Prefix -- makes the iterator point to the item just before
+ current(), and makes it the new current item for the iterator. If
+ current() was the first item, operator--() returns 0.
+*/
diff --git a/src/qt3support/tools/q3intdict.h b/src/qt3support/tools/q3intdict.h
new file mode 100644
index 0000000..fbd1114
--- /dev/null
+++ b/src/qt3support/tools/q3intdict.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3INTDICT_H
+#define Q3INTDICT_H
+
+#include <Qt3Support/q3gdict.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3IntDict
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GDict
+#endif
+{
+public:
+ Q3IntDict(int size=17) : Q3GDict(size,IntKey,0,0) {}
+ Q3IntDict( const Q3IntDict<type> &d ) : Q3GDict(d) {}
+ ~Q3IntDict() { clear(); }
+ Q3IntDict<type> &operator=(const Q3IntDict<type> &d)
+ { return (Q3IntDict<type>&)Q3GDict::operator=(d); }
+ uint count() const { return Q3GDict::count(); }
+ uint size() const { return Q3GDict::size(); }
+ bool isEmpty() const { return Q3GDict::count() == 0; }
+ void insert( long k, const type *d )
+ { Q3GDict::look_int(k,(Item)d,1); }
+ void replace( long k, const type *d )
+ { Q3GDict::look_int(k,(Item)d,2); }
+ bool remove( long k ) { return Q3GDict::remove_int(k); }
+ type *take( long k ) { return (type*)Q3GDict::take_int(k); }
+ type *find( long k ) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_int(k,0,0); }
+ type *operator[]( long k ) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_int(k,0,0); }
+ void clear() { Q3GDict::clear(); }
+ void resize( uint n ) { Q3GDict::resize(n); }
+ void statistics() const { Q3GDict::statistics(); }
+
+#ifdef qdoc
+protected:
+ virtual QDataStream& read( QDataStream &, Q3PtrCollection::Item & );
+ virtual QDataStream& write( QDataStream &, Q3PtrCollection::Item ) const;
+#endif
+
+private:
+ void deleteItem( Item d );
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3IntDict<void>::deleteItem( Q3PtrCollection::Item )
+{
+}
+#endif
+
+template<class type> inline void Q3IntDict<type>::deleteItem( Q3PtrCollection::Item d )
+{
+ if ( del_item ) delete (type*)d;
+}
+
+template<class type>
+class Q3IntDictIterator : public Q3GDictIterator
+{
+public:
+ Q3IntDictIterator(const Q3IntDict<type> &d) :Q3GDictIterator((Q3GDict &)d) {}
+ ~Q3IntDictIterator() {}
+ uint count() const { return dict->count(); }
+ bool isEmpty() const { return dict->count() == 0; }
+ type *toFirst() { return (type *)Q3GDictIterator::toFirst(); }
+ operator type *() const { return (type *)Q3GDictIterator::get(); }
+ type *current() const { return (type *)Q3GDictIterator::get(); }
+ long currentKey() const { return Q3GDictIterator::getKeyInt(); }
+ type *operator()() { return (type *)Q3GDictIterator::operator()(); }
+ type *operator++() { return (type *)Q3GDictIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GDictIterator::operator+=(j);}
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3INTDICT_H
diff --git a/src/qt3support/tools/q3intdict.qdoc b/src/qt3support/tools/q3intdict.qdoc
new file mode 100644
index 0000000..f108f30
--- /dev/null
+++ b/src/qt3support/tools/q3intdict.qdoc
@@ -0,0 +1,376 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3IntDict
+ \brief The Q3IntDict class is a template class that provides a dictionary based on long keys.\
+ \compat
+
+ Q3IntDict is implemented as a template class. Define a template
+ instance Q3IntDict\<X\> to create a dictionary that operates on
+ pointers to X (X*).
+
+ A dictionary is a collection of key-value pairs. The key is an \c
+ long used for insertion, removal and lookup. The value is a
+ pointer. Dictionaries provide very fast insertion and lookup.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3intdict.cpp 0
+
+ See Q3Dict for full details, including the choice of dictionary
+ size, and how deletions are handled.
+
+ \sa Q3IntDictIterator, Q3Dict, Q3AsciiDict, Q3PtrDict
+*/
+
+
+/*!
+ \fn Q3IntDict::Q3IntDict( int size )
+
+ Constructs a dictionary using an internal hash array of size \a
+ size.
+
+ Setting \a size to a suitably large prime number (equal to or
+ greater than the expected number of entries) makes the hash
+ distribution better which leads to faster lookup.
+*/
+
+/*!
+ \fn Q3IntDict::Q3IntDict( const Q3IntDict<type> &dict )
+
+ Constructs a copy of \a dict.
+
+ Each item in \a dict is inserted into this dictionary. Only the
+ pointers are copied (shallow copy).
+*/
+
+/*!
+ \fn Q3IntDict::~Q3IntDict()
+
+ Removes all items from the dictionary and destroys it.
+
+ All iterators that access this dictionary will be reset.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn Q3IntDict<type> &Q3IntDict::operator=(const Q3IntDict<type> &dict)
+
+ Assigns \a dict to this dictionary and returns a reference to this
+ dictionary.
+
+ This dictionary is first cleared and then each item in \a dict is
+ inserted into this dictionary. Only the pointers are copied
+ (shallow copy), unless newItem() has been reimplemented.
+*/
+
+/*!
+ \fn uint Q3IntDict::count() const
+
+ Returns the number of items in the dictionary.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn uint Q3IntDict::size() const
+
+ Returns the size of the internal hash array (as specified in the
+ constructor).
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3IntDict::resize( uint newsize )
+
+ Changes the size of the hashtable to \a newsize. The contents of
+ the dictionary are preserved, but all iterators on the dictionary
+ become invalid.
+*/
+
+/*!
+ \fn bool Q3IntDict::isEmpty() const
+
+ Returns TRUE if the dictionary is empty; otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3IntDict::insert( long key, const type *item )
+
+ Insert item \a item into the dictionary using key \a key.
+
+ Multiple items can have the same key, in which case only the last
+ item will be accessible using \l operator[]().
+
+ \a item may not be 0.
+
+ \sa replace()
+*/
+
+/*!
+ \fn void Q3IntDict::replace( long key, const type *item )
+
+ If the dictionary has key \a key, this key's item is replaced with
+ \a item. If the dictionary doesn't contain key \a key, \a item is
+ inserted into the dictionary using key \a key.
+
+ \a item may not be 0.
+
+ Equivalent to:
+ \snippet doc/src/snippets/code/doc_src_q3intdict.cpp 1
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be replaced.
+
+ \sa insert()
+*/
+
+/*!
+ \fn bool Q3IntDict::remove( long key )
+
+ Removes the item associated with \a key from the dictionary.
+ Returns TRUE if successful, i.e. if the \a key is in the
+ dictionary; otherwise returns FALSE.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be removed.
+
+ The removed item is deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that refer to the removed item will be
+ set to point to the next item in the dictionary's traversal
+ order.
+
+ \sa take(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3IntDict::take( long key )
+
+ Takes the item associated with \a key out of the dictionary
+ without deleting it (even if \link Q3PtrCollection::setAutoDelete()
+ auto-deletion\endlink is enabled).
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be taken.
+
+ Returns a pointer to the item taken out, or 0 if the key does not
+ exist in the dictionary.
+
+ All dictionary iterators that refer to the taken item will be set
+ to point to the next item in the dictionary's traversing order.
+
+ \sa remove(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn void Q3IntDict::clear()
+
+ Removes all items from the dictionary.
+
+ The removed items are deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that access this dictionary will be reset.
+
+ \sa remove(), take(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3IntDict::find( long key ) const
+
+ Returns the item associated with \a key, or 0 if the key does not
+ exist in the dictionary.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be found.
+
+ Equivalent to operator[].
+
+ \sa operator[]()
+*/
+
+/*!
+ \fn type *Q3IntDict::operator[]( long key ) const
+
+ Returns the item associated with \a key, or 0 if the key does not
+ exist in the dictionary.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be found.
+
+ Equivalent to the find() function.
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3IntDict::statistics() const
+
+ Debugging-only function that prints out the dictionary
+ distribution using qDebug().
+*/
+
+/*!
+ \fn QDataStream& Q3IntDict::read( QDataStream &s, Q3PtrCollection::Item &item )
+
+ Reads a dictionary item from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3IntDict::write( QDataStream &s, Q3PtrCollection::Item item ) const
+
+ Writes a dictionary \a item to the stream \a s and returns a
+ reference to the stream.
+
+ \sa read()
+*/
+
+/*!
+ \class Q3IntDictIterator
+ \brief The Q3IntDictIterator class provides an iterator for Q3IntDict collections.
+ \compat
+
+ Q3IntDictIterator is implemented as a template class. Define a
+ template instance Q3IntDictIterator\<X\> to create a dictionary
+ iterator that operates on Q3IntDict\<X\> (dictionary of X*).
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3intdict.cpp 2
+
+ Note that the traversal order is arbitrary; you are not guaranteed the
+ order shown above.
+
+ Multiple iterators may independently traverse the same dictionary.
+ A Q3IntDict knows about all the iterators that are operating on the
+ dictionary. When an item is removed from the dictionary, Q3IntDict
+ updates all iterators that refer the removed item to point to the
+ next item in the traversal order.
+
+ \sa Q3IntDict
+*/
+
+/*!
+ \fn Q3IntDictIterator::Q3IntDictIterator( const Q3IntDict<type> &dict )
+
+ Constructs an iterator for \a dict. The current iterator item is
+ set to point to the 'first' item in the \a dict. The first item
+ refers to the first item in the dictionary's arbitrary internal
+ ordering.
+*/
+
+/*!
+ \fn Q3IntDictIterator::~Q3IntDictIterator()
+
+ Destroys the iterator.
+*/
+
+/*!
+ \fn uint Q3IntDictIterator::count() const
+
+ Returns the number of items in the dictionary this iterator
+ operates over.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3IntDictIterator::isEmpty() const
+
+ Returns TRUE if the dictionary is empty; otherwise eturns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn type *Q3IntDictIterator::toFirst()
+
+ Sets the current iterator item to point to the first item in the
+ dictionary and returns a pointer to the item. The first item
+ refers to the first item in the dictionary's arbitrary internal
+ ordering. If the dictionary is empty it sets the current item to
+ 0 and returns 0.
+*/
+
+/*!
+ \fn Q3IntDictIterator::operator type *() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3IntDictIterator::current() const
+
+ Returns a pointer to the current iterator item.
+*/
+
+/*!
+ \fn long Q3IntDictIterator::currentKey() const
+
+ Returns the key for the current iterator item.
+*/
+
+/*!
+ \fn type *Q3IntDictIterator::operator()()
+
+ Makes the succeeding item current and returns the original current
+ item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3IntDictIterator::operator++()
+
+ Prefix ++ makes the succeeding item current and returns the new
+ current item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3IntDictIterator::operator+=( uint jump )
+
+ Sets the current item to the item \a jump positions after the
+ current item, and returns a pointer to that item.
+
+ If that item is beyond the last item or if the dictionary is
+ empty, it sets the current item to 0 and returns 0.
+*/
diff --git a/src/qt3support/tools/q3memarray.h b/src/qt3support/tools/q3memarray.h
new file mode 100644
index 0000000..ca29bca
--- /dev/null
+++ b/src/qt3support/tools/q3memarray.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3MEMARRAY_H
+#define Q3MEMARRAY_H
+
+#include <Qt3Support/q3garray.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3MemArray : public Q3GArray
+{
+public:
+ typedef type* Iterator;
+ typedef const type* ConstIterator;
+ typedef type ValueType;
+
+protected:
+ Q3MemArray(int, int) : Q3GArray(0, 0) {}
+
+public:
+ Q3MemArray() {}
+ Q3MemArray(int size) : Q3GArray(size*sizeof(type)) {}
+ Q3MemArray(const Q3MemArray<type> &a) : Q3GArray(a) {}
+ Q3MemArray(const QVector<type> &vector);
+ ~Q3MemArray() {}
+ Q3MemArray<type> &operator=(const Q3MemArray<type> &a)
+ { return (Q3MemArray<type>&)Q3GArray::assign(a); }
+ type *data() const { return (type *)Q3GArray::data(); }
+ uint nrefs() const { return Q3GArray::nrefs(); }
+ uint size() const { return Q3GArray::size()/sizeof(type); }
+ uint count() const { return size(); }
+ bool isEmpty() const { return Q3GArray::size() == 0; }
+ bool isNull() const { return Q3GArray::data() == 0; }
+ bool resize(uint size) { return Q3GArray::resize(size*sizeof(type)); }
+ bool resize(uint size, Optimization optim) { return Q3GArray::resize(size*sizeof(type), optim); }
+ bool truncate(uint pos) { return Q3GArray::resize(pos*sizeof(type)); }
+ bool fill(const type &d, int size = -1)
+ { return Q3GArray::fill((char*)&d,size,sizeof(type)); }
+ void detach() { Q3GArray::detach(); }
+ Q3MemArray<type> copy() const
+ { Q3MemArray<type> tmp; return tmp.duplicate(*this); }
+ Q3MemArray<type>& assign(const Q3MemArray<type>& a)
+ { return (Q3MemArray<type>&)Q3GArray::assign(a); }
+ Q3MemArray<type>& assign(const type *a, uint n)
+ { return (Q3MemArray<type>&)Q3GArray::assign((char*)a,n*sizeof(type)); }
+ Q3MemArray<type>& duplicate(const Q3MemArray<type>& a)
+ { return (Q3MemArray<type>&)Q3GArray::duplicate(a); }
+ Q3MemArray<type>& duplicate(const type *a, uint n)
+ { return (Q3MemArray<type>&)Q3GArray::duplicate((char*)a,n*sizeof(type)); }
+ Q3MemArray<type>& setRawData(const type *a, uint n)
+ { return (Q3MemArray<type>&)Q3GArray::setRawData((char*)a,
+ n*sizeof(type)); }
+ void resetRawData(const type *a, uint n)
+ { Q3GArray::resetRawData((char*)a,n*sizeof(type)); }
+ int find(const type &d, uint i=0) const
+ { return Q3GArray::find((char*)&d,i,sizeof(type)); }
+ int contains(const type &d) const
+ { return Q3GArray::contains((char*)&d,sizeof(type)); }
+ void sort() { Q3GArray::sort(sizeof(type)); }
+ int bsearch(const type &d) const
+ { return Q3GArray::bsearch((const char*)&d,sizeof(type)); }
+ // ### Qt 4.0: maybe provide uint overload as work-around for MSVC bug
+ type& operator[](int i) const
+ { return (type &)(*(type *)Q3GArray::at(i*sizeof(type))); }
+ type& at(uint i) const
+ { return (type &)(*(type *)Q3GArray::at(i*sizeof(type))); }
+ operator const type*() const { return (const type *)Q3GArray::data(); }
+ bool operator==(const Q3MemArray<type> &a) const { return isEqual(a); }
+ bool operator!=(const Q3MemArray<type> &a) const { return !isEqual(a); }
+ Iterator begin() { return data(); }
+ Iterator end() { return data() + size(); }
+ ConstIterator begin() const { return data(); }
+ ConstIterator end() const { return data() + size(); }
+
+ operator QVector<type>() const;
+};
+
+template<class type>
+Q_OUTOFLINE_TEMPLATE Q3MemArray<type>::Q3MemArray(const QVector<type> &vector)
+ : Q3GArray(vector.size()*sizeof(type))
+{
+ for (int i = 0; i < vector.size(); ++i)
+ at(i) = vector.at(i);
+}
+
+template<class type>
+Q_OUTOFLINE_TEMPLATE Q3MemArray<type>::operator QVector<type>() const
+{
+ QVector<type> vector;
+ for (int i = 0; i < size(); ++i)
+ vector.append(at(i));
+ return vector;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3MEMARRAY_H
diff --git a/src/qt3support/tools/q3memarray.qdoc b/src/qt3support/tools/q3memarray.qdoc
new file mode 100644
index 0000000..5d6f9b2
--- /dev/null
+++ b/src/qt3support/tools/q3memarray.qdoc
@@ -0,0 +1,509 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3MemArray
+ \brief The Q3MemArray class is a template class that provides arrays of simple types.
+ \compat
+
+ Q3MemArray is implemented as a template class. Define a template
+ instance Q3MemArray\<X\> to create an array that contains X items.
+
+ Q3MemArray stores the array elements directly in the array. It can
+ only deal with simple types (i.e. C++ types, structs, and classes
+ that have no constructors, destructors, or virtual functions).
+ Q3MemArray uses bitwise operations to copy and compare array
+ elements.
+
+ The Q3PtrVector collection class is also a kind of array. Like most
+ old Qt collection classes, it uses pointers to the contained items.
+
+ Q3MemArray uses explicit sharing with a
+ reference count. If more than one array shares common data and one
+ of the arrays is modified, all the arrays are modified.
+
+ The benefit of sharing is that a program does not need to duplicate
+ data when it is not required, which results in lower memory use
+ and less copying of data.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3memarray.cpp 0
+
+ Program output:
+ \snippet doc/src/snippets/code/doc_src_q3memarray.qdoc 1
+
+ Note concerning the use of Q3MemArray for manipulating structs or
+ classes: Compilers will often pad the size of structs of odd sizes
+ up to the nearest word boundary. This will then be the size
+ Q3MemArray will use for its bitwise element comparisons. Because
+ the remaining bytes will typically be uninitialized, this can
+ cause find() etc. to fail to find the element. Example:
+
+ \snippet doc/src/snippets/code/doc_src_q3memarray.cpp 2
+
+ To work around this, make sure that you use a struct where
+ sizeof() returns the same as the sum of the sizes of the members
+ either by changing the types of the struct members or by adding
+ dummy members.
+
+ Q3MemArray data can be traversed by iterators (see begin() and
+ end()). The number of items is returned by count(). The array can
+ be resized with resize() and filled using fill().
+
+ You can make a shallow copy of the array with assign() (or
+ operator=()) and a deep copy with duplicate().
+
+ Search for values in the array with find() and contains(). For
+ sorted arrays (see sort()) you can search using bsearch().
+
+ You can set the data directly using setRawData() and
+ resetRawData(), although this requires care.
+*/
+
+/*! \fn Q3MemArray::operator QVector<type>() const
+
+ Automatically converts the Q3MemArray<type> into a QVector<type>.
+*/
+
+/*! \typedef Q3MemArray::Iterator
+ A Q3MemArray iterator.
+ \sa begin() end()
+*/
+/*! \typedef Q3MemArray::ConstIterator
+ A const Q3MemArray iterator.
+ \sa begin() end()
+*/
+/*! \typedef Q3MemArray::ValueType
+ \internal
+*/
+
+/*!
+ \fn Q3MemArray::Q3MemArray()
+
+ Constructs a null array.
+
+ \sa isNull()
+*/
+
+/*!
+ \fn Q3MemArray::Q3MemArray( int size )
+
+ Constructs an array with room for \a size elements. Makes a null
+ array if \a size == 0.
+
+ The elements are left uninitialized.
+
+ \sa resize(), isNull()
+*/
+
+/*!
+ \fn Q3MemArray::Q3MemArray( const Q3MemArray<type> &a )
+
+ Constructs a shallow copy of \a a.
+
+ \sa assign()
+*/
+
+/*!
+ \fn Q3MemArray::Q3MemArray(const QVector<type> &vector)
+
+ Constructs a copy of \a vector.
+*/
+
+/*!
+ \fn Q3MemArray::Q3MemArray(int arg1, int arg2)
+
+ Constructs an array \e{without allocating} array space. The
+ arguments \a arg1 and \a arg2 should be zero. Use at your own
+ risk.
+*/
+
+/*!
+ \fn Q3MemArray::~Q3MemArray()
+
+ Dereferences the array data and deletes it if this was the last
+ reference.
+*/
+
+/*!
+ \fn Q3MemArray<type> &Q3MemArray::operator=( const Q3MemArray<type> &a )
+
+ Assigns a shallow copy of \a a to this array and returns a
+ reference to this array.
+
+ Equivalent to assign( a ).
+*/
+
+/*!
+ \fn type *Q3MemArray::data() const
+
+ Returns a pointer to the actual array data.
+
+ The array is a null array if data() == 0 (null pointer).
+
+ \sa isNull()
+*/
+
+/*!
+ \fn uint Q3MemArray::nrefs() const
+
+ Returns the reference count for the shared array data. This
+ reference count is always greater than zero.
+*/
+
+/*!
+ \fn uint Q3MemArray::size() const
+
+ Returns the size of the array (maximum number of elements).
+
+ The array is a null array if size() == 0.
+
+ \sa isNull(), resize()
+*/
+
+/*!
+ \fn uint Q3MemArray::count() const
+
+ Returns the same as size().
+
+ \sa size()
+*/
+
+/*!
+ \fn bool Q3MemArray::isEmpty() const
+
+ Returns TRUE if the array is empty; otherwise returns FALSE.
+
+ isEmpty() is equivalent to isNull() for Q3MemArray (unlike
+ QString).
+*/
+
+/*!
+ \fn bool Q3MemArray::isNull() const
+
+ Returns TRUE if the array is null; otherwise returns FALSE.
+
+ A null array has size() == 0 and data() == 0.
+*/
+
+/*!
+ \fn bool Q3MemArray::resize( uint size, Optimization optim )
+
+ Resizes (expands or shrinks) the array to \a size elements. The
+ array becomes a null array if \a size == 0.
+
+ Returns TRUE if successful, or FALSE if the memory cannot be
+ allocated.
+
+ New elements are not initialized.
+
+ \a optim is either \c MemOptim (the default) or
+ \c SpeedOptim. When optimizing for speed rather than memory
+ consumption, the array uses a smart grow and shrink algorithm that
+ might allocate more memory than is actually needed for \a size
+ elements. This speeds up subsequent resize operations, for example
+ when appending many elements to an array, since the space has
+ already been allocated.
+
+ \sa size()
+*/
+
+/*!
+ \fn bool Q3MemArray::resize( uint size )
+
+ \overload
+
+ Resizes (expands or shrinks) the array to \a size elements. The
+ array becomes a null array if \a size == 0.
+
+ Returns TRUE if successful, i.e. if the memory can be allocated;
+ otherwise returns FALSE.
+
+ New elements are not initialized.
+
+ \sa size()
+*/
+
+/*!
+ \fn bool Q3MemArray::truncate( uint pos )
+
+ Truncates the array at position \a pos.
+
+ Returns TRUE if successful, i.e. if the memory can be allocated;
+ otherwise returns FALSE.
+
+ Equivalent to resize(\a pos).
+
+ \sa resize()
+*/
+
+/*!
+ \fn bool Q3MemArray::fill( const type &v, int size )
+
+ Fills the array with the value \a v. If \a size is specified as
+ different from -1, then the array will be resized before being
+ filled.
+
+ Returns TRUE if successful, i.e. if \a size is -1, or \a size is
+ != -1 and the memory can be allocated; otherwise returns FALSE.
+
+ \sa resize()
+*/
+
+/*!
+ \fn void Q3MemArray::detach()
+
+ Detaches this array from shared array data; i.e. it makes a
+ private, deep copy of the data.
+
+ Copying will be performed only if the \link nrefs() reference
+ count\endlink is greater than one.
+
+ \sa copy()
+*/
+
+/*!
+ \fn Q3MemArray<type> Q3MemArray::copy() const
+
+ Returns a deep copy of this array.
+
+ \sa detach(), duplicate()
+*/
+
+/*!
+ \fn Q3MemArray<type> &Q3MemArray::assign( const Q3MemArray<type> &a )
+
+ Shallow copy. Dereferences the current array and references the
+ data contained in \a a instead. Returns a reference to this array.
+
+ \sa operator=()
+*/
+
+/*!
+ \fn Q3MemArray<type> &Q3MemArray::assign( const type *data, uint size )
+
+ \overload
+
+ Shallow copy. Dereferences the current array and references the
+ array data \a data, which contains \a size elements. Returns a
+ reference to this array.
+
+ Do not delete \a data later; Q3MemArray will call free() on it
+ at the right time.
+*/
+
+/*!
+ \fn Q3MemArray<type> &Q3MemArray::duplicate( const Q3MemArray<type> &a )
+
+ Deep copy. Dereferences the current array and obtains a copy of
+ the data contained in \a a instead. Returns a reference to this
+ array.
+
+ \sa copy()
+*/
+
+/*!
+ \fn Q3MemArray<type> &Q3MemArray::duplicate( const type *data, uint size )
+
+ \overload
+
+ Deep copy. Dereferences the current array and obtains a copy of
+ the array data \a data instead. Returns a reference to this array.
+ The size of the array is given by \a size.
+
+ \sa copy()
+*/
+
+/*!
+ \fn Q3MemArray<type> &Q3MemArray::setRawData( const type *data, uint size )
+
+ Sets raw data and returns a reference to the array.
+
+ Dereferences the current array and sets the new array data to \a
+ data and the new array size to \a size. Do not attempt to resize
+ or re-assign the array data when raw data has been set. Call
+ resetRawData(\a data, \a size) to reset the array.
+
+ Setting raw data is useful because it sets Q3MemArray data without
+ allocating memory or copying data.
+
+ Example I (intended use):
+ \snippet doc/src/snippets/code/doc_src_q3memarray.cpp 3
+
+ Example II (you don't want to do this):
+ \snippet doc/src/snippets/code/doc_src_q3memarray.cpp 4
+
+ \warning If you do not call resetRawData(), Q3MemArray will attempt
+ to deallocate or reallocate the raw data, which might not be too
+ good. Be careful.
+
+ \sa resetRawData()
+*/
+
+/*!
+ \fn void Q3MemArray::resetRawData( const type *data, uint size )
+
+ Removes internal references to the raw data that was set using
+ setRawData(). This means that Q3MemArray no longer has access to
+ the \a data, so you are free to manipulate \a data as you wish.
+ You can now use the Q3MemArray without affecting the original \a
+ data, for example by calling setRawData() with a pointer to some
+ other data.
+
+ The arguments must be the \a data and length, \a size, that were
+ passed to setRawData(). This is for consistency checking.
+
+ \sa setRawData()
+*/
+
+/*!
+ \fn int Q3MemArray::find( const type &v, uint index ) const
+
+ Finds the first occurrence of \a v, starting at position \a index.
+
+ Returns the position of \a v, or -1 if \a v could not be found.
+
+ \sa contains()
+*/
+
+/*!
+ \fn int Q3MemArray::contains( const type &v ) const
+
+ Returns the number of times \a v occurs in the array.
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3MemArray::sort()
+
+ Sorts the array elements in ascending order, using bitwise
+ comparison (memcmp()).
+
+ \sa bsearch()
+*/
+
+/*!
+ \fn int Q3MemArray::bsearch( const type &v ) const
+
+ In a sorted array (as sorted by sort()), finds the first
+ occurrence of \a v by using a binary search. For a sorted
+ array this is generally much faster than find(), which does
+ a linear search.
+
+ Returns the position of \a v, or -1 if \a v could not be found.
+
+ \sa sort(), find()
+*/
+
+/*!
+ \fn type &Q3MemArray::operator[]( int index ) const
+
+ Returns a reference to the element at position \a index in the
+ array.
+
+ This can be used to both read and set an element. Equivalent to
+ at().
+
+ \sa at()
+*/
+
+/*!
+ \fn type &Q3MemArray::at( uint index ) const
+
+ Returns a reference to the element at position \a index in the array.
+
+ This can be used to both read and set an element.
+
+ \sa operator[]()
+*/
+
+/*!
+ \fn Q3MemArray::operator const type *() const
+
+ Cast operator. Returns a pointer to the array.
+
+ \sa data()
+*/
+
+/*!
+ \fn bool Q3MemArray::operator==( const Q3MemArray<type> &a ) const
+
+ Returns TRUE if this array is equal to \a a; otherwise returns
+ FALSE.
+
+ The two arrays are compared bitwise.
+
+ \sa operator!=()
+*/
+
+/*!
+ \fn bool Q3MemArray::operator!=( const Q3MemArray<type> &a ) const
+
+ Returns TRUE if this array is different from \a a; otherwise
+ returns FALSE.
+
+ The two arrays are compared bitwise.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn Iterator Q3MemArray::begin()
+
+ Returns an iterator pointing at the beginning of this array. This
+ iterator can be used in the same way as the iterators of
+ Q3ValueList and QMap, for example.
+*/
+
+/*!
+ \fn Iterator Q3MemArray::end()
+
+ Returns an iterator pointing behind the last element of this
+ array. This iterator can be used in the same way as the iterators
+ of Q3ValueList and QMap, for example.
+*/
+
+/*!
+ \fn ConstIterator Q3MemArray::begin() const
+
+ \overload
+
+ Returns a const iterator pointing at the beginning of this array.
+ This iterator can be used in the same way as the iterators of
+ Q3ValueList and QMap, for example.
+*/
+
+/*!
+ \fn ConstIterator Q3MemArray::end() const
+
+ \overload
+
+ Returns a const iterator pointing behind the last element of this
+ array. This iterator can be used in the same way as the iterators
+ of Q3ValueList and QMap, for example.
+*/
diff --git a/src/qt3support/tools/q3objectdict.h b/src/qt3support/tools/q3objectdict.h
new file mode 100644
index 0000000..9061174
--- /dev/null
+++ b/src/qt3support/tools/q3objectdict.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3OBJECTDICT_H
+#define Q3OBJECTDICT_H
+
+#include <QtCore/qmetaobject.h>
+#include <Qt3Support/q3asciidict.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+//
+// The object dictionary is a collection of QMetaObjects
+//
+
+class Q3ObjectDictionary : public Q3AsciiDict<QMetaObject>
+{
+public:
+ Q3ObjectDictionary(int size=17,bool cs=true,bool ck=true)
+ : Q3AsciiDict<QMetaObject>(size,cs,ck) {}
+ Q3ObjectDictionary( const Q3ObjectDictionary &dict )
+ : Q3AsciiDict<QMetaObject>(dict) {}
+ ~Q3ObjectDictionary() { clear(); }
+ Q3ObjectDictionary &operator=(const Q3ObjectDictionary &dict)
+ { return (Q3ObjectDictionary&)Q3AsciiDict<QMetaObject>::operator=(dict);}
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3OBJECTDICT_H
diff --git a/src/qt3support/tools/q3ptrcollection.cpp b/src/qt3support/tools/q3ptrcollection.cpp
new file mode 100644
index 0000000..8a98f57
--- /dev/null
+++ b/src/qt3support/tools/q3ptrcollection.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3ptrcollection.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3PtrCollection
+ \reentrant
+ \brief The Q3PtrCollection class is the base class of most pointer-based Qt collections.
+
+ \compat
+
+ The Q3PtrCollection class is an abstract base class for the Qt
+ collection classes QDict, Q3PtrList,
+ etc.
+
+ A Q3PtrCollection only knows about the number of objects in the
+ collection and the deletion strategy (see setAutoDelete()).
+
+ A collection is implemented using the \c Item (generic collection
+ item) type, which is a \c void*. The template classes that create
+ the real collections cast the \c Item to the required type.
+*/
+
+
+/*!
+ \typedef Q3PtrCollection::Item
+
+ This type is the generic "item" in a Q3PtrCollection.
+*/
+
+
+/*!
+ \fn Q3PtrCollection::Q3PtrCollection()
+
+ Constructs a collection. The constructor is protected because
+ Q3PtrCollection is an abstract class.
+*/
+
+/*!
+ \fn Q3PtrCollection::Q3PtrCollection( const Q3PtrCollection & source )
+
+ Constructs a copy of \a source with autoDelete() set to false. The
+ constructor is protected because Q3PtrCollection is an abstract
+ class.
+
+ Note that if \a source has autoDelete turned on, copying it will
+ risk memory leaks, reading freed memory, or both.
+*/
+
+/*!
+ \fn Q3PtrCollection::~Q3PtrCollection()
+
+ Destroys the collection. The destructor is protected because
+ Q3PtrCollection is an abstract class.
+*/
+
+
+/*!
+ \fn bool Q3PtrCollection::autoDelete() const
+
+ Returns the setting of the auto-delete option. The default is false.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn void Q3PtrCollection::setAutoDelete( bool enable )
+
+ Sets the collection to auto-delete its contents if \a enable is
+ true and to never delete them if \a enable is false.
+
+ If auto-deleting is turned on, all the items in a collection are
+ deleted when the collection itself is deleted. This is convenient
+ if the collection has the only pointer to the items.
+
+ The default setting is false, for safety. If you turn it on, be
+ careful about copying the collection - you might find yourself
+ with two collections deleting the same items.
+
+ Note that the auto-delete setting may also affect other functions
+ in subclasses. For example, a subclass that has a remove()
+ function will remove the item from its data structure, and if
+ auto-delete is enabled, will also delete the item.
+
+ \sa autoDelete()
+*/
+
+
+/*!
+ \fn virtual uint Q3PtrCollection::count() const
+
+ Returns the number of objects in the collection.
+*/
+
+/*!
+ \fn virtual void Q3PtrCollection::clear()
+
+ Removes all objects from the collection. The objects will be
+ deleted if auto-delete has been enabled.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn void Q3PtrCollection::deleteItem( Item d )
+
+ Reimplement this function if you want to be able to delete items.
+
+ Deletes an item that is about to be removed from the collection.
+
+ This function has to reimplemented in the collection template
+ classes, and should \e only delete item \a d if auto-delete has
+ been enabled.
+
+ \warning If you reimplement this function you must also
+ reimplement the destructor and call the virtual function clear()
+ from your destructor. This is due to the way virtual functions and
+ destructors work in C++: Virtual functions in derived classes
+ cannot be called from a destructor. If you do not do this, your
+ deleteItem() function will not be called when the container is
+ destroyed.
+
+ \sa newItem(), setAutoDelete()
+*/
+
+/*!
+ Virtual function that creates a copy of an object that is about to
+ be inserted into the collection.
+
+ The default implementation returns the \a d pointer, i.e. no copy
+ is made.
+
+ This function is seldom reimplemented in the collection template
+ classes. It is not common practice to make a copy of something
+ that is being inserted.
+
+ \sa deleteItem()
+*/
+
+Q3PtrCollection::Item Q3PtrCollection::newItem(Item d)
+{
+ return d; // just return reference
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3ptrcollection.h b/src/qt3support/tools/q3ptrcollection.h
new file mode 100644
index 0000000..46d3fac
--- /dev/null
+++ b/src/qt3support/tools/q3ptrcollection.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PTRCOLLECTION_H
+#define Q3PTRCOLLECTION_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3GVector;
+class Q3GList;
+class Q3GDict;
+
+class Q_COMPAT_EXPORT Q3PtrCollection // inherited by all collections
+{
+public:
+ bool autoDelete() const { return del_item; }
+ void setAutoDelete(bool enable) { del_item = enable; }
+
+ virtual uint count() const = 0;
+ virtual void clear() = 0; // delete all objects
+
+ typedef void *Item; // generic collection item
+
+protected:
+ Q3PtrCollection() { del_item = false; } // no deletion of objects
+ Q3PtrCollection(const Q3PtrCollection &) { del_item = false; }
+ virtual ~Q3PtrCollection() {}
+
+ bool del_item; // default false
+
+ virtual Item newItem(Item); // create object
+ virtual void deleteItem(Item) = 0; // delete object
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PTRCOLLECTION_H
diff --git a/src/qt3support/tools/q3ptrdict.h b/src/qt3support/tools/q3ptrdict.h
new file mode 100644
index 0000000..2e76f6d
--- /dev/null
+++ b/src/qt3support/tools/q3ptrdict.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PTRDICT_H
+#define Q3PTRDICT_H
+
+#include <Qt3Support/q3gdict.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3PtrDict
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GDict
+#endif
+{
+public:
+ Q3PtrDict(int size=17) : Q3GDict(size,PtrKey,0,0) {}
+ Q3PtrDict( const Q3PtrDict<type> &d ) : Q3GDict(d) {}
+ ~Q3PtrDict() { clear(); }
+ Q3PtrDict<type> &operator=(const Q3PtrDict<type> &d)
+ { return (Q3PtrDict<type>&)Q3GDict::operator=(d); }
+ uint count() const { return Q3GDict::count(); }
+ uint size() const { return Q3GDict::size(); }
+ bool isEmpty() const { return Q3GDict::count() == 0; }
+ void insert( void *k, const type *d )
+ { Q3GDict::look_ptr(k,(Item)d,1); }
+ void replace( void *k, const type *d )
+ { Q3GDict::look_ptr(k,(Item)d,2); }
+ bool remove( void *k ) { return Q3GDict::remove_ptr(k); }
+ type *take( void *k ) { return (type*)Q3GDict::take_ptr(k); }
+ type *find( void *k ) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_ptr(k,0,0); }
+ type *operator[]( void *k ) const
+ { return (type *)((Q3GDict*)this)->Q3GDict::look_ptr(k,0,0); }
+ void clear() { Q3GDict::clear(); }
+ void resize( uint n ) { Q3GDict::resize(n); }
+ void statistics() const { Q3GDict::statistics(); }
+
+#ifdef qdoc
+protected:
+ virtual QDataStream& read( QDataStream &, Q3PtrCollection::Item & );
+ virtual QDataStream& write( QDataStream &, Q3PtrCollection::Item ) const;
+#endif
+
+private:
+ void deleteItem( Item d );
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3PtrDict<void>::deleteItem( Q3PtrCollection::Item )
+{
+}
+#endif
+
+template<class type>
+inline void Q3PtrDict<type>::deleteItem( Q3PtrCollection::Item d )
+{
+ if ( del_item ) delete (type *)d;
+}
+
+template<class type>
+class Q3PtrDictIterator : public Q3GDictIterator
+{
+public:
+ Q3PtrDictIterator(const Q3PtrDict<type> &d) :Q3GDictIterator((Q3GDict &)d) {}
+ ~Q3PtrDictIterator() {}
+ uint count() const { return dict->count(); }
+ bool isEmpty() const { return dict->count() == 0; }
+ type *toFirst() { return (type *)Q3GDictIterator::toFirst(); }
+ operator type *() const { return (type *)Q3GDictIterator::get(); }
+ type *current() const { return (type *)Q3GDictIterator::get(); }
+ void *currentKey() const { return Q3GDictIterator::getKeyPtr(); }
+ type *operator()() { return (type *)Q3GDictIterator::operator()(); }
+ type *operator++() { return (type *)Q3GDictIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GDictIterator::operator+=(j);}
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PTRDICT_H
diff --git a/src/qt3support/tools/q3ptrdict.qdoc b/src/qt3support/tools/q3ptrdict.qdoc
new file mode 100644
index 0000000..21dcdfd
--- /dev/null
+++ b/src/qt3support/tools/q3ptrdict.qdoc
@@ -0,0 +1,374 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3PtrDict
+ \brief The Q3PtrDict class is a template class that provides a dictionary based on void* keys.
+ \compat
+
+ Q3PtrDict is implemented as a template class. Define a template
+ instance Q3PtrDict\<X\> to create a dictionary that operates on
+ pointers to X (X*).
+
+ A dictionary is a collection of key-value pairs. The key is a
+ void* used for insertion, removal and lookup. The value is a
+ pointer. Dictionaries provide very fast insertion and lookup.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3ptrdict.cpp 0
+ In this example we use a dictionary to add an extra property (a
+ char*) to the line edits we're using.
+
+ See Q3Dict for full details, including the choice of dictionary
+ size, and how deletions are handled.
+
+ \sa Q3PtrDictIterator, Q3Dict, Q3AsciiDict, Q3IntDict
+*/
+
+
+/*!
+ \fn Q3PtrDict::Q3PtrDict( int size )
+
+ Constructs a dictionary using an internal hash array with the size
+ \a size.
+
+ Setting \a size to a suitably large prime number (equal to or
+ greater than the expected number of entries) makes the hash
+ distribution better and improves lookup performance.
+*/
+
+/*!
+ \fn Q3PtrDict::Q3PtrDict( const Q3PtrDict<type> &dict )
+
+ Constructs a copy of \a dict.
+
+ Each item in \a dict is inserted into this dictionary. Only the
+ pointers are copied (shallow copy).
+*/
+
+/*!
+ \fn Q3PtrDict::~Q3PtrDict()
+
+ Removes all items from the dictionary and destroys it.
+
+ All iterators that access this dictionary will be reset.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn Q3PtrDict<type> &Q3PtrDict::operator=(const Q3PtrDict<type> &dict)
+
+ Assigns \a dict to this dictionary and returns a reference to this
+ dictionary.
+
+ This dictionary is first cleared and then each item in \a dict is
+ inserted into the dictionary. Only the pointers are copied
+ (shallow copy), unless newItem() has been reimplemented.
+*/
+
+/*!
+ \fn uint Q3PtrDict::count() const
+
+ Returns the number of items in the dictionary.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn uint Q3PtrDict::size() const
+
+ Returns the size of the internal hash table (as specified in the
+ constructor).
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3PtrDict::resize( uint newsize )
+
+ Changes the size of the hash table to \a newsize. The contents of
+ the dictionary are preserved, but all iterators on the dictionary
+ become invalid.
+*/
+
+/*!
+ \fn bool Q3PtrDict::isEmpty() const
+
+ Returns TRUE if the dictionary is empty; otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn void Q3PtrDict::insert( void *key, const type *item )
+
+ Inserts the \a key with the \a item into the dictionary.
+
+ Multiple items can have the same key, in which case only the last
+ item will be accessible using \l operator[]().
+
+ \a item may not be 0.
+
+ \sa replace()
+*/
+
+/*!
+ \fn void Q3PtrDict::replace( void *key, const type *item )
+
+ If the dictionary has key \a key, this key's item is replaced with
+ \a item. If the dictionary doesn't contain key \a key, \a item is
+ inserted into the dictionary using key \a key.
+
+ \a item may not be 0.
+
+ Equivalent to
+ \snippet doc/src/snippets/code/doc_src_q3ptrdict.cpp 1
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be replaced.
+
+ \sa insert()
+*/
+
+/*!
+ \fn bool Q3PtrDict::remove( void *key )
+
+ Removes the item associated with \a key from the dictionary.
+ Returns TRUE if successful, i.e. if \a key is in the dictionary;
+ otherwise returns FALSE.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be removed.
+
+ The removed item is deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that refer to the removed item will be
+ set to point to the next item in the dictionary traversal order.
+
+ \sa take(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3PtrDict::take( void *key )
+
+ Takes the item associated with \a key out of the dictionary
+ without deleting it (even if \link Q3PtrCollection::setAutoDelete()
+ auto-deletion\endlink is enabled).
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be removed.
+
+ Returns a pointer to the item taken out, or 0 if the key does not
+ exist in the dictionary.
+
+ All dictionary iterators that refer to the taken item will be set
+ to point to the next item in the dictionary traversal order.
+
+ \sa remove(), clear(), setAutoDelete()
+*/
+
+/*!
+ \fn void Q3PtrDict::clear()
+
+ Removes all items from the dictionary.
+
+ The removed items are deleted if \link
+ Q3PtrCollection::setAutoDelete() auto-deletion\endlink is enabled.
+
+ All dictionary iterators that access this dictionary will be
+ reset.
+
+ \sa remove(), take(), setAutoDelete()
+*/
+
+/*!
+ \fn type *Q3PtrDict::find( void *key ) const
+
+ Returns the item associated with \a key, or 0 if the key does not
+ exist in the dictionary.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be found.
+
+ Equivalent to operator[].
+
+ \sa operator[]()
+*/
+
+/*!
+ \fn type *Q3PtrDict::operator[]( void *key ) const
+
+ Returns the item associated with \a key, or 0 if the key does not
+ exist in the dictionary.
+
+ If there are two or more items with equal keys, then the most
+ recently inserted item will be found.
+
+ Equivalent to the find() function.
+
+ \sa find()
+*/
+
+/*!
+ \fn void Q3PtrDict::statistics() const
+
+ Debugging-only function that prints out the dictionary
+ distribution using qDebug().
+*/
+
+/*!
+ \fn QDataStream& Q3PtrDict::read( QDataStream &s, Q3PtrCollection::Item &item )
+
+ Reads a dictionary item from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3PtrDict::write( QDataStream &s, Q3PtrCollection::Item item) const
+
+ Writes a dictionary \a item to the stream \a s and returns a
+ reference to the stream.
+
+ \sa read()
+*/
+
+/*!
+ \class Q3PtrDictIterator
+ \brief The Q3PtrDictIterator class provides an iterator for Q3PtrDict collections.
+ \compat
+
+ Q3PtrDictIterator is implemented as a template class. Define a
+ template instance Q3PtrDictIterator\<X\> to create a dictionary
+ iterator that operates on Q3PtrDict\<X\> (dictionary of X*).
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3ptrdict.cpp 2
+ In the example we insert some line edits into a dictionary,
+ associating a string with each. We then iterate over the
+ dictionary printing the associated strings.
+
+ Multiple iterators may independently traverse the same dictionary.
+ A Q3PtrDict knows about all the iterators that are operating on the
+ dictionary. When an item is removed from the dictionary, Q3PtrDict
+ updates all iterators that refer the removed item to point to the
+ next item in the traversing order.
+
+ \sa Q3PtrDict
+*/
+
+/*!
+ \fn Q3PtrDictIterator::Q3PtrDictIterator( const Q3PtrDict<type> &dict )
+
+ Constructs an iterator for \a dict. The current iterator item is
+ set to point on the first item in the \a dict.
+*/
+
+/*!
+ \fn Q3PtrDictIterator::~Q3PtrDictIterator()
+
+ Destroys the iterator.
+*/
+
+/*!
+ \fn uint Q3PtrDictIterator::count() const
+
+ Returns the number of items in the dictionary this iterator
+ operates on.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3PtrDictIterator::isEmpty() const
+
+ Returns TRUE if the dictionary is empty; otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn type *Q3PtrDictIterator::toFirst()
+
+ Sets the current iterator item to point to the first item in the
+ dictionary and returns a pointer to the item. If the dictionary is
+ empty, it sets the current item to 0 and returns 0.
+*/
+
+/*!
+ \fn Q3PtrDictIterator::operator type *() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3PtrDictIterator::current() const
+
+ Returns a pointer to the current iterator item's value.
+*/
+
+/*!
+ \fn void *Q3PtrDictIterator::currentKey() const
+
+ Returns the current iterator item's key.
+*/
+
+/*!
+ \fn type *Q3PtrDictIterator::operator()()
+
+ Makes the succeeding item current and returns the original current
+ item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3PtrDictIterator::operator++()
+
+ Prefix ++ makes the succeeding item current and returns the new
+ current item.
+
+ If the current iterator item was the last item in the dictionary
+ or if it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3PtrDictIterator::operator+=( uint jump )
+
+ Sets the current item to the item \a jump positions after the
+ current item and returns a pointer to that item.
+
+ If that item is beyond the last item or if the dictionary is
+ empty, it sets the current item to 0 and returns 0.
+*/
diff --git a/src/qt3support/tools/q3ptrlist.h b/src/qt3support/tools/q3ptrlist.h
new file mode 100644
index 0000000..d0e398f
--- /dev/null
+++ b/src/qt3support/tools/q3ptrlist.h
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PTRLIST_H
+#define Q3PTRLIST_H
+
+#include <Qt3Support/q3glist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3PtrListStdIterator : public Q3GListStdIterator
+{
+public:
+ inline Q3PtrListStdIterator( Q3LNode* n ): Q3GListStdIterator(n) {}
+ type *operator*() { return node ? (type *)node->getData() : 0; }
+ inline Q3PtrListStdIterator<type> operator++()
+ { node = next(); return *this; }
+ inline Q3PtrListStdIterator<type> operator++(int)
+ { Q3LNode* n = node; node = next(); return Q3PtrListStdIterator<type>( n ); }
+ inline bool operator==( const Q3PtrListStdIterator<type>& it ) const { return node == it.node; }
+ inline bool operator!=( const Q3PtrListStdIterator<type>& it ) const { return node != it.node; }
+};
+
+
+template<class type>
+class Q3PtrList
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GList
+#endif
+{
+public:
+
+ Q3PtrList() {}
+ Q3PtrList( const Q3PtrList<type> &l ) : Q3GList(l) {}
+ ~Q3PtrList() { clear(); }
+ Q3PtrList<type> &operator=(const Q3PtrList<type> &l)
+ { return (Q3PtrList<type>&)Q3GList::operator=(l); }
+ bool operator==( const Q3PtrList<type> &list ) const
+ { return Q3GList::operator==( list ); }
+ bool operator!=( const Q3PtrList<type> &list ) const
+ { return !Q3GList::operator==( list ); }
+ uint count() const { return Q3GList::count(); }
+ bool isEmpty() const { return Q3GList::count() == 0; }
+ bool insert( uint i, const type *d){ return Q3GList::insertAt(i,(Q3PtrCollection::Item)d); }
+ void inSort( const type *d ) { Q3GList::inSort((Q3PtrCollection::Item)d); }
+ void prepend( const type *d ) { Q3GList::insertAt(0,(Q3PtrCollection::Item)d); }
+ void append( const type *d ) { Q3GList::append((Q3PtrCollection::Item)d); }
+ bool remove( uint i ) { return Q3GList::removeAt(i); }
+ bool remove() { return Q3GList::remove((Q3PtrCollection::Item)0); }
+ bool remove( const type *d ) { return Q3GList::remove((Q3PtrCollection::Item)d); }
+ bool removeRef( const type *d ) { return Q3GList::removeRef((Q3PtrCollection::Item)d); }
+ void removeNode( Q3LNode *n ) { Q3GList::removeNode(n); }
+ bool removeFirst() { return Q3GList::removeFirst(); }
+ bool removeLast() { return Q3GList::removeLast(); }
+ type *take( uint i ) { return (type *)Q3GList::takeAt(i); }
+ type *take() { return (type *)Q3GList::take(); }
+ type *takeNode( Q3LNode *n ) { return (type *)Q3GList::takeNode(n); }
+ void clear() { Q3GList::clear(); }
+ void sort() { Q3GList::sort(); }
+ int find( const type *d ) { return Q3GList::find((Q3PtrCollection::Item)d); }
+ int findNext( const type *d ) { return Q3GList::find((Q3PtrCollection::Item)d,false); }
+ int findRef( const type *d ) { return Q3GList::findRef((Q3PtrCollection::Item)d); }
+ int findNextRef( const type *d ){ return Q3GList::findRef((Q3PtrCollection::Item)d,false);}
+ uint contains( const type *d ) const { return Q3GList::contains((Q3PtrCollection::Item)d); }
+ uint containsRef( const type *d ) const
+ { return Q3GList::containsRef((Q3PtrCollection::Item)d); }
+ bool replace( uint i, const type *d ) { return Q3GList::replaceAt( i, (Q3PtrCollection::Item)d ); }
+ type *at( uint i ) { return (type *)Q3GList::at(i); }
+ int at() const { return Q3GList::at(); }
+ type *current() const { return (type *)Q3GList::get(); }
+ Q3LNode *currentNode() const { return Q3GList::currentNode(); }
+ type *getFirst() const { return (type *)Q3GList::cfirst(); }
+ type *getLast() const { return (type *)Q3GList::clast(); }
+ type *first() { return (type *)Q3GList::first(); }
+ type *last() { return (type *)Q3GList::last(); }
+ type *next() { return (type *)Q3GList::next(); }
+ type *prev() { return (type *)Q3GList::prev(); }
+ void toVector( Q3GVector *vec )const{ Q3GList::toVector(vec); }
+
+
+ // standard iterators
+ typedef Q3PtrListStdIterator<type> Iterator;
+ typedef Q3PtrListStdIterator<type> ConstIterator;
+ inline Iterator begin() { return Q3GList::begin(); }
+ inline ConstIterator begin() const { return Q3GList::begin(); }
+ inline ConstIterator constBegin() const { return Q3GList::begin(); }
+ inline Iterator end() { return Q3GList::end(); }
+ inline ConstIterator end() const { return Q3GList::end(); }
+ inline ConstIterator constEnd() const { return Q3GList::end(); }
+ inline Iterator erase( Iterator it ) { return Q3GList::erase( it ); }
+ // stl syntax compatibility
+ typedef Iterator iterator;
+ typedef ConstIterator const_iterator;
+
+
+#ifdef qdoc
+protected:
+ virtual int compareItems( Q3PtrCollection::Item, Q3PtrCollection::Item );
+ virtual QDataStream& read( QDataStream&, Q3PtrCollection::Item& );
+ virtual QDataStream& write( QDataStream&, Q3PtrCollection::Item ) const;
+#endif
+
+private:
+ void deleteItem( Item d );
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3PtrList<void>::deleteItem( Q3PtrCollection::Item )
+{
+}
+#endif
+
+template<class type> inline void Q3PtrList<type>::deleteItem( Q3PtrCollection::Item d )
+{
+ if ( del_item ) delete (type *)d;
+}
+
+template<class type>
+class Q3PtrListIterator : public Q3GListIterator
+{
+public:
+ Q3PtrListIterator(const Q3PtrList<type> &l) :Q3GListIterator((Q3GList &)l) {}
+ ~Q3PtrListIterator() {}
+ uint count() const { return list->count(); }
+ bool isEmpty() const { return list->count() == 0; }
+ bool atFirst() const { return Q3GListIterator::atFirst(); }
+ bool atLast() const { return Q3GListIterator::atLast(); }
+ type *toFirst() { return (type *)Q3GListIterator::toFirst(); }
+ type *toLast() { return (type *)Q3GListIterator::toLast(); }
+ operator type *() const { return (type *)Q3GListIterator::get(); }
+ type *operator*() { return (type *)Q3GListIterator::get(); }
+
+ // No good, since Q3PtrList<char> (ie. QStrList fails...
+ //
+ // MSVC++ gives warning
+ // Sunpro C++ 4.1 gives error
+ // type *operator->() { return (type *)Q3GListIterator::get(); }
+
+ type *current() const { return (type *)Q3GListIterator::get(); }
+ type *operator()() { return (type *)Q3GListIterator::operator()();}
+ type *operator++() { return (type *)Q3GListIterator::operator++(); }
+ type *operator+=(uint j) { return (type *)Q3GListIterator::operator+=(j);}
+ type *operator--() { return (type *)Q3GListIterator::operator--(); }
+ type *operator-=(uint j) { return (type *)Q3GListIterator::operator-=(j);}
+ Q3PtrListIterator<type>& operator=(const Q3PtrListIterator<type>&it)
+ { Q3GListIterator::operator=(it); return *this; }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PTRLIST_H
diff --git a/src/qt3support/tools/q3ptrlist.qdoc b/src/qt3support/tools/q3ptrlist.qdoc
new file mode 100644
index 0000000..e19d6bf
--- /dev/null
+++ b/src/qt3support/tools/q3ptrlist.qdoc
@@ -0,0 +1,1144 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3PtrList
+ \brief The Q3PtrList class is a template class that provides a list.
+ \compat
+
+ Q3ValueList is an STL-compatible alternative to this class.
+
+ Define a template instance Q3PtrList\<X\> to create a list that
+ operates on pointers to X (X*).
+
+ The list class is indexable and has a \link at() current
+ index\endlink and a \link current() current item\endlink. The
+ first item corresponds to index position 0. The current index is
+ -1 if the current item is 0.
+
+ Items are inserted with prepend(), insert() or append(). Items are
+ removed with remove(), removeRef(), removeFirst() and
+ removeLast(). You can search for an item using find(), findNext(),
+ findRef() or findNextRef(). The list can be sorted with sort().
+ You can count the number of occurrences of an item with contains()
+ or containsRef(). You can get a pointer to the current item with
+ current(), to an item at a particular index position in the list
+ with at() or to the first or last item with getFirst() and
+ getLast(). You can also iterate over the list with first(),
+ last(), next() and prev() (which all update current()). The list's
+ deletion property is set with setAutoDelete().
+
+ \target example
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3ptrlist.cpp 0
+
+ The output is
+ \snippet doc/src/snippets/code/doc_src_q3ptrlist.cpp 1
+
+ Q3PtrList has several member functions for traversing the list, but
+ using a Q3PtrListIterator can be more practical. Multiple list
+ iterators may traverse the same list, independently of each other
+ and of the current list item.
+
+ In the example above we make the call setAutoDelete(true).
+ Enabling auto-deletion tells the list to delete items that are
+ removed. The default is to not delete items when they are removed
+ but this would cause a memory leak in the example because there
+ are no other references to the list items.
+
+ When inserting an item into a list only the pointer is copied, not
+ the item itself, i.e. a shallow copy. It is possible to make the
+ list copy all of the item's data (deep copy) when an item is
+ inserted. insert(), inSort() and append() call the virtual
+ function Q3PtrCollection::newItem() for the item to be inserted.
+ Inherit a list and reimplement newItem() to have deep copies.
+
+ When removing an item from a list, the virtual function
+ Q3PtrCollection::deleteItem() is called. Q3PtrList's default
+ implementation is to delete the item if auto-deletion is enabled.
+
+ The virtual function compareItems() can be reimplemented to
+ compare two list items. This function is called from all list
+ functions that need to compare list items, for instance
+ remove(const type*). If you only want to deal with pointers, there
+ are functions that compare pointers instead, for instance
+ removeRef(const type*). These functions are somewhat faster than
+ those that call compareItems().
+
+ List items are stored as \c void* in an internal Q3LNode, which
+ also holds pointers to the next and previous list items. The
+ functions currentNode(), removeNode(), and takeNode() operate
+ directly on the Q3LNode, but they should be used with care. The
+ data component of the node is available through Q3LNode's getData()
+ function.
+
+ The Q3StrList class is a list of \c char*.
+ It reimplements newItem(), deleteItem() and compareItems(). (But
+ see QStringList for a list of Unicode QStrings.)
+
+ \sa Q3PtrListIterator
+*/
+
+
+/*!
+ \fn Q3PtrList::Q3PtrList()
+
+ Constructs an empty list.
+*/
+
+/*!
+ \fn Q3PtrList::Q3PtrList( const Q3PtrList<type> &list )
+
+ Constructs a copy of \a list.
+
+ Each item in \a list is \link append() appended\endlink to this
+ list. Only the pointers are copied (shallow copy).
+*/
+
+/*!
+ \fn Q3PtrList::~Q3PtrList()
+
+ Removes all items from the list and destroys the list.
+
+ All list iterators that access this list will be reset.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn Q3PtrList<type> &Q3PtrList::operator=(const Q3PtrList<type> &list)
+
+ Assigns \a list to this list and returns a reference to this list.
+
+ This list is first cleared and then each item in \a list is \link
+ append() appended\endlink to this list. Only the pointers are
+ copied (shallow copy) unless newItem() has been reimplemented.
+*/
+
+/*!
+ \fn bool Q3PtrList::operator==(const Q3PtrList<type> &list ) const
+
+ Compares this list with \a list. Returns TRUE if the lists contain
+ the same data; otherwise returns FALSE.
+*/
+
+/*!
+ \fn uint Q3PtrList::count() const
+
+ Returns the number of items in the list.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3PtrList::operator!=(const Q3PtrList<type> &list ) const
+
+ Compares this list with \a list. Returns TRUE if the lists contain
+ different data; otherwise returns FALSE.
+*/
+
+
+/*!
+ \fn void Q3PtrList::sort()
+
+ Sorts the list by the result of the virtual compareItems()
+ function.
+
+ The heap sort algorithm is used for sorting. It sorts n items with
+ O(n*log n) comparisons. This is the asymptotic optimal solution of
+ the sorting problem.
+
+ If the items in your list support operator<() and operator==(),
+ you might be better off with Q3SortedList because it implements the
+ compareItems() function for you using these two operators.
+
+ \sa inSort()
+*/
+
+/*!
+ \fn bool Q3PtrList::isEmpty() const
+
+ Returns TRUE if the list is empty; otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn bool Q3PtrList::insert( uint index, const type *item )
+
+ Inserts the \a item at position \a index in the list.
+
+ Returns TRUE if successful, i.e. if \a index is in range;
+ otherwise returns FALSE. The valid range is 0 to count()
+ (inclusively). The item is appended if \a index == count().
+
+ The inserted item becomes the current list item.
+
+ \a item must not be 0.
+
+ \sa append(), current(), replace()
+*/
+
+/*!
+ \fn bool Q3PtrList::replace( uint index, const type *item )
+
+ Replaces the item at position \a index with the new \a item.
+
+ Returns TRUE if successful, i.e. \a index is in the range 0 to
+ count()-1.
+
+ \sa append(), current(), insert()
+*/
+
+/*!
+ \fn void Q3PtrList::inSort( const type *item )
+
+ Inserts the \a item at its sorted position in the list.
+
+ The sort order depends on the virtual compareItems() function. All
+ items must be inserted with inSort() to maintain the sorting
+ order.
+
+ The inserted item becomes the current list item.
+
+ \a item must not be 0.
+
+ \warning Using inSort() is slow. An alternative, especially if you
+ have lots of items, is to simply append() or insert() them and
+ then use sort(). inSort() takes up to O(n) compares. That means
+ inserting n items in your list will need O(n^2) compares whereas
+ sort() only needs O(n*log n) for the same task. So use inSort()
+ only if you already have a presorted list and want to insert just
+ a few additional items.
+
+ \sa insert(), compareItems(), current(), sort()
+*/
+
+/*!
+ \fn void Q3PtrList::append( const type *item )
+
+ Inserts the \a item at the end of the list.
+
+ The inserted item becomes the current list item. This is
+ equivalent to \c{insert( count(), item )}.
+
+ \a item must not be 0.
+
+ \sa insert(), current(), prepend()
+*/
+
+/*!
+ \fn void Q3PtrList::prepend( const type *item )
+
+ Inserts the \a item at the start of the list.
+
+ The inserted item becomes the current list item. This is
+ equivalent to \c{insert( 0, item )}.
+
+ \a item must not be 0.
+
+ \sa append(), insert(), current()
+*/
+
+/*!
+ \fn bool Q3PtrList::remove( uint index )
+
+ Removes the item at position \a index in the list.
+
+ Returns TRUE if successful, i.e. if \a index is in range;
+ otherwise returns FALSE. The valid range is \c{0..(count() - 1)}
+ inclusive.
+
+ The removed item is deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ The item after the removed item becomes the new current list item
+ if the removed item is not the last item in the list. If the last
+ item is removed, the new last item becomes the current item.
+
+ All list iterators that refer to the removed item will be set to
+ point to the new current item.
+
+ \sa take(), clear(), setAutoDelete(), current() removeRef()
+*/
+
+/*!
+ \fn bool Q3PtrList::remove()
+
+ \overload
+
+ Removes the current list item.
+
+ Returns TRUE if successful, i.e. if the current item isn't 0;
+ otherwise returns FALSE.
+
+ The removed item is deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ The item after the removed item becomes the new current list item
+ if the removed item is not the last item in the list. If the last
+ item is removed, the new last item becomes the current item. The
+ current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the removed item will be set to
+ point to the new current item.
+
+ \sa take(), clear(), setAutoDelete(), current() removeRef()
+*/
+
+/*!
+ \fn bool Q3PtrList::remove( const type *item )
+
+ \overload
+
+ Removes the first occurrence of \a item from the list.
+
+ Returns TRUE if successful, i.e. if \a item is in the list;
+ otherwise returns FALSE.
+
+ The removed item is deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ The compareItems() function is called when searching for the item
+ in the list. If compareItems() is not reimplemented, it is more
+ efficient to call removeRef().
+
+ If \a item is NULL then the current item is removed from the list.
+
+ The item after the removed item becomes the new current list item
+ if the removed item is not the last item in the list. If the last
+ item is removed, the new last item becomes the current item. The
+ current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the removed item will be set to
+ point to the new current item.
+
+ \sa removeRef(), take(), clear(), setAutoDelete(), compareItems(),
+ current()
+*/
+
+/*!
+ \fn bool Q3PtrList::removeRef( const type *item )
+
+ Removes the first occurrence of \a item from the list.
+
+ Returns TRUE if successful, i.e. if \a item is in the list;
+ otherwise returns FALSE.
+
+ The removed item is deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ Equivalent to:
+ \snippet doc/src/snippets/code/doc_src_q3ptrlist.cpp 2
+
+ The item after the removed item becomes the new current list item
+ if the removed item is not the last item in the list. If the last
+ item is removed, the new last item becomes the current item. The
+ current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the removed item will be set to
+ point to the new current item.
+
+ \sa remove(), clear(), setAutoDelete(), current()
+*/
+
+/*!
+ \fn void Q3PtrList::removeNode( Q3LNode *node )
+
+ Removes the \a node from the list.
+
+ This node must exist in the list, otherwise the program may crash.
+
+ The removed item is deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ The first item in the list will become the new current list item.
+ The current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the removed item will be set to
+ point to the item succeeding this item or to the preceding item if
+ the removed item was the last item.
+
+ \warning Do not call this function unless you are an expert.
+
+ \sa takeNode(), currentNode() remove() removeRef()
+*/
+
+/*!
+ \fn bool Q3PtrList::removeFirst()
+
+ Removes the first item from the list. Returns TRUE if successful,
+ i.e. if the list isn't empty; otherwise returns FALSE.
+
+ The removed item is deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ The first item in the list becomes the new current list item. The
+ current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the removed item will be set to
+ point to the new current item.
+
+ \sa removeLast(), setAutoDelete(), current() remove()
+*/
+
+/*!
+ \fn bool Q3PtrList::removeLast()
+
+ Removes the last item from the list. Returns TRUE if successful,
+ i.e. if the list isn't empty; otherwise returns FALSE.
+
+ The removed item is deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ The last item in the list becomes the new current list item. The
+ current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the removed item will be set to
+ point to the new current item.
+
+ \sa removeFirst(), setAutoDelete(), current()
+*/
+
+/*!
+ \fn type *Q3PtrList::take( uint index )
+
+ Takes the item at position \a index out of the list without
+ deleting it (even if \link setAutoDelete() auto-deletion\endlink
+ is enabled).
+
+ Returns a pointer to the item taken out of the list, or 0 if the
+ index is out of range. The valid range is \c{0..(count() - 1)}
+ inclusive.
+
+ The item after the removed item becomes the new current list item
+ if the removed item is not the last item in the list. If the last
+ item is removed, the new last item becomes the current item. The
+ current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the taken item will be set to
+ point to the new current item.
+
+ \sa remove(), clear(), current()
+*/
+
+/*!
+ \fn type *Q3PtrList::take()
+
+ \overload
+
+ Takes the current item out of the list without deleting it (even
+ if \link setAutoDelete() auto-deletion\endlink is enabled).
+
+ Returns a pointer to the item taken out of the list, or 0 if
+ the current item is 0.
+
+ The item after the removed item becomes the new current list item
+ if the removed item is not the last item in the list. If the last
+ item is removed, the new last item becomes the current item. The
+ current item is set to 0 if the list becomes empty.
+
+ All list iterators that refer to the taken item will be set to
+ point to the new current item.
+
+ \sa remove(), clear(), current()
+*/
+
+/*!
+ \fn type *Q3PtrList::takeNode( Q3LNode *node )
+
+ Takes the \a node out of the list without deleting its item (even
+ if \link setAutoDelete() auto-deletion\endlink is enabled).
+ Returns a pointer to the item taken out of the list.
+
+ This node must exist in the list, otherwise the program may crash.
+
+ The first item in the list becomes the new current list item.
+
+ All list iterators that refer to the taken item will be set to
+ point to the item succeeding this item or to the preceding item if
+ the taken item was the last item.
+
+ \warning Do not call this function unless you are an expert.
+
+ \sa removeNode(), currentNode()
+*/
+
+/*!
+ \fn void Q3PtrList::clear()
+
+ Removes all items from the list.
+
+ The removed items are deleted if \link setAutoDelete()
+ auto-deletion\endlink is enabled.
+
+ All list iterators that access this list will be reset.
+
+ \sa remove(), take(), setAutoDelete()
+*/
+
+/*!
+ \fn int Q3PtrList::find( const type *item )
+
+ Finds the first occurrence of \a item in the list.
+
+ If the item is found, the list sets the current item to point to
+ the found item and returns the index of this item. If the item is
+ not found, the list sets the current item to 0, the current
+ index to -1, and returns -1.
+
+ The compareItems() function is called when searching for the item
+ in the list. If compareItems() is not reimplemented, it is more
+ efficient to call findRef().
+
+ \sa findNext(), findRef(), compareItems(), current()
+*/
+
+/*!
+ \fn int Q3PtrList::findNext( const type *item )
+
+ Finds the next occurrence of \a item in the list, starting from
+ the current list item.
+
+ If the item is found, the list sets the current item to point to
+ the found item and returns the index of this item. If the item is
+ not found, the list sets the current item to 0, the current
+ index to -1, and returns -1.
+
+ The compareItems() function is called when searching for the item
+ in the list. If compareItems() is not reimplemented, it is more
+ efficient to call findNextRef().
+
+ \sa find(), findNextRef(), compareItems(), current()
+*/
+
+/*!
+ \fn int Q3PtrList::findRef( const type *item )
+
+ Finds the first occurrence of \a item in the list.
+
+ If the item is found, the list sets the current item to point to
+ the found item and returns the index of this item. If the item is
+ not found, the list sets the current item to 0, the current
+ index to -1, and returns -1.
+
+ Calling this function is much faster than find() because find()
+ compares \a item with each list item using compareItems(), whereas
+ this function only compares the pointers.
+
+ \sa findNextRef(), find(), current()
+*/
+
+/*!
+ \fn int Q3PtrList::findNextRef( const type *item )
+
+ Finds the next occurrence of \a item in the list, starting from
+ the current list item.
+
+ If the item is found, the list sets the current item to point to
+ the found item and returns the index of this item. If the item is
+ not found, the list sets the current item to 0, the current
+ index to -1, and returns -1.
+
+ Calling this function is much faster than findNext() because
+ findNext() compares \a item with each list item using
+ compareItems(), whereas this function only compares the pointers.
+
+ \sa findRef(), findNext(), current()
+*/
+
+/*!
+ \fn uint Q3PtrList::contains( const type *item ) const
+
+ Returns the number of occurrences of \a item in the list.
+
+ The compareItems() function is called when looking for the \a item
+ in the list. If compareItems() is not reimplemented, it is more
+ efficient to call containsRef().
+
+ This function does not affect the current list item.
+
+ \sa containsRef(), compareItems()
+*/
+
+/*!
+ \fn uint Q3PtrList::containsRef( const type *item ) const
+
+ Returns the number of occurrences of \a item in the list.
+
+ Calling this function is much faster than contains() because
+ contains() compares \a item with each list item using
+ compareItems(), whereas his function only compares the pointers.
+
+ This function does not affect the current list item.
+
+ \sa contains()
+*/
+
+/*!
+ \fn type *Q3PtrList::at( uint index )
+
+ Returns a pointer to the item at position \a index in the list, or
+ 0 if the index is out of range.
+
+ Sets the current list item to this item if \a index is valid. The
+ valid range is \c{0..(count() - 1)} inclusive.
+
+ This function is very efficient. It starts scanning from the first
+ item, last item, or current item, whichever is closest to \a
+ index.
+
+ \sa current()
+*/
+
+/*!
+ \fn int Q3PtrList::at() const
+
+ \overload
+
+ Returns the index of the current list item. The returned value is
+ -1 if the current item is 0.
+
+ \sa current()
+*/
+
+/*!
+ \fn type *Q3PtrList::current() const
+
+ Returns a pointer to the current list item. The current item may
+ be 0 (implies that the current index is -1).
+
+ \sa at()
+*/
+
+/*!
+ \fn Q3LNode *Q3PtrList::currentNode() const
+
+ Returns a pointer to the current list node.
+
+ The node can be kept and removed later using removeNode(). The
+ advantage is that the item can be removed directly without
+ searching the list.
+
+ \warning Do not call this function unless you are an expert.
+
+ \sa removeNode(), takeNode(), current()
+*/
+
+/*!
+ \fn type *Q3PtrList::getFirst() const
+
+ Returns a pointer to the first item in the list, or 0 if the list
+ is empty.
+
+ This function does not affect the current list item.
+
+ \sa first(), getLast()
+*/
+
+/*!
+ \fn type *Q3PtrList::getLast() const
+
+ Returns a pointer to the last item in the list, or 0 if the list
+ is empty.
+
+ This function does not affect the current list item.
+
+ \sa last(), getFirst()
+*/
+
+/*!
+ \fn type *Q3PtrList::first()
+
+ Returns a pointer to the first item in the list and makes this the
+ current list item; returns 0 if the list is empty.
+
+ \sa getFirst(), last(), next(), prev(), current()
+*/
+
+/*!
+ \fn type *Q3PtrList::last()
+
+ Returns a pointer to the last item in the list and makes this the
+ current list item; returns 0 if the list is empty.
+
+ \sa getLast(), first(), next(), prev(), current()
+*/
+
+/*!
+ \fn type *Q3PtrList::next()
+
+ Returns a pointer to the item succeeding the current item. Returns
+ 0 if the current item is 0 or equal to the last item.
+
+ Makes the succeeding item current. If the current item before this
+ function call was the last item, the current item will be set to
+ 0. If the current item was 0, this function does nothing.
+
+ \sa first(), last(), prev(), current()
+*/
+
+/*!
+ \fn type *Q3PtrList::prev()
+
+ Returns a pointer to the item preceding the current item. Returns
+ 0 if the current item is 0 or equal to the first item.
+
+ Makes the preceding item current. If the current item before this
+ function call was the first item, the current item will be set to
+ 0. If the current item was 0, this function does nothing.
+
+ \sa first(), last(), next(), current()
+*/
+
+/*!
+ \fn void Q3PtrList::toVector( Q3GVector *vec ) const
+
+ Stores all list items in the vector \a vec.
+
+ The vector must be of the same item type, otherwise the result
+ will be undefined.
+*/
+
+/*!
+ \typedef Q3PtrList::iterator
+
+ \internal
+*/
+
+/*!
+ \typedef Q3PtrList::Iterator
+
+ \internal
+*/
+
+/*!
+ \typedef Q3PtrList::ConstIterator
+
+ \internal
+*/
+
+/*!
+ \typedef Q3PtrList::const_iterator
+
+ \internal
+*/
+
+/*!
+ \fn Q3PtrList::constBegin() const
+
+ \internal
+*/
+
+/*!
+ \fn Q3PtrList::constEnd() const
+
+ \internal
+*/
+
+/*!
+ \fn Q3PtrList::erase(Iterator)
+
+ \internal
+*/
+
+
+/*****************************************************************************
+ Q3PtrListIterator documentation
+ *****************************************************************************/
+
+/*!
+ \class Q3PtrListIterator
+ \brief The Q3PtrListIterator class provides an iterator for
+ Q3PtrList collections.
+ \compat
+
+ Define a template instance Q3PtrListIterator\<X\> to create a list
+ iterator that operates on Q3PtrList\<X\> (list of X*).
+
+ The following example is similar to the
+ example in the Q3PtrList class documentation,
+ but it uses Q3PtrListIterator. The class Employee is
+ defined there.
+
+ \snippet doc/src/snippets/code/doc_src_q3ptrlist.cpp 3
+
+ The output is
+ \snippet doc/src/snippets/code/doc_src_q3ptrlist.cpp 4
+
+ Using a list iterator is a more robust way of traversing the list
+ than using the Q3PtrList member functions \link Q3PtrList::first()
+ first\endlink(), \link Q3PtrList::next() next\endlink(), \link
+ Q3PtrList::current() current\endlink(), etc., as many iterators can
+ traverse the same list independently.
+
+ An iterator has its own current list item and can get the next and
+ previous list items. It doesn't modify the list in any way.
+
+ When an item is removed from the list, all iterators that point to
+ that item are updated to point to Q3PtrList::current() instead to
+ avoid dangling references.
+
+ \sa Q3PtrList
+*/
+
+/*!
+ \fn Q3PtrListIterator::Q3PtrListIterator( const Q3PtrList<type> &list )
+
+ Constructs an iterator for \a list. The current iterator item is
+ set to point on the first item in the \a list.
+*/
+
+/*!
+ \fn Q3PtrListIterator::~Q3PtrListIterator()
+
+ Destroys the iterator.
+*/
+
+/*!
+ \fn uint Q3PtrListIterator::count() const
+
+ Returns the number of items in the list this iterator operates on.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn bool Q3PtrListIterator::isEmpty() const
+
+ Returns TRUE if the list is empty; otherwise returns FALSE.
+
+ \sa count()
+*/
+
+/*!
+ \fn bool Q3PtrListIterator::atFirst() const
+
+ Returns TRUE if the current iterator item is the first list item;
+ otherwise returns FALSE.
+
+ \sa toFirst(), atLast()
+*/
+
+/*!
+ \fn bool Q3PtrListIterator::atLast() const
+
+ Returns TRUE if the current iterator item is the last list item;
+ otherwise returns FALSE.
+
+ \sa toLast(), atFirst()
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::toFirst()
+
+ Sets the current iterator item to point to the first list item and
+ returns a pointer to the item. Sets the current item to 0 and
+ returns 0 if the list is empty.
+
+ \sa toLast(), atFirst()
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::toLast()
+
+ Sets the current iterator item to point to the last list item and
+ returns a pointer to the item. Sets the current item to 0 and
+ returns 0 if the list is empty.
+
+ \sa toFirst(), atLast()
+*/
+
+/*!
+ \fn Q3PtrListIterator::operator type *() const
+
+ Cast operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::operator*()
+
+ Asterisk operator. Returns a pointer to the current iterator item.
+ Same as current().
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::current() const
+
+ Returns a pointer to the current iterator item. If the iterator is
+ positioned before the first item in the list or after the last
+ item in the list, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::operator()()
+
+ Makes the succeeding item current and returns the original current
+ item.
+
+ If the current iterator item was the last item in the list or if
+ it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::operator++()
+
+ Prefix ++ makes the succeeding item current and returns the new
+ current item.
+
+ If the current iterator item was the last item in the list or if
+ it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::operator+=( uint jump )
+
+ Sets the current item to the item \a jump positions after the
+ current item and returns a pointer to that item.
+
+ If that item is beyond the last item or if the list is empty, it
+ sets the current item to 0 and returns 0
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::operator--()
+
+ Prefix - makes the preceding item current and returns the new
+ current item.
+
+ If the current iterator item was the first item in the list or if
+ it was 0, 0 is returned.
+*/
+
+/*!
+ \fn type *Q3PtrListIterator::operator-=( uint jump )
+
+ Returns the item \a jump positions before the current item or 0
+ if it is beyond the first item. Makes this the current item.
+*/
+
+/*!
+ \fn Q3PtrListIterator<type>& Q3PtrListIterator::operator=( const Q3PtrListIterator<type> &it )
+
+ Assignment. Makes a copy of the iterator \a it and returns a
+ reference to this iterator.
+*/
+
+/*!
+ \class Q3StrList
+ \brief The Q3StrList class provides a doubly-linked list of char*.
+ \compat
+
+ If you want a string list of \l{QString}s use QStringList.
+
+ This class is a Q3PtrList\<char\> instance (a list of char*).
+
+ Q3StrList can make deep or shallow copies of the strings that are
+ inserted.
+
+ A deep copy means that memory is allocated for the string and then
+ the string data is copied into that memory. A shallow copy is just
+ a copy of the pointer value and not of the string data itself.
+
+ The disadvantage of shallow copies is that because a pointer can
+ be deleted only once, the program must put all strings in a
+ central place and know when it is safe to delete them (i.e. when
+ the strings are no longer referenced by other parts of the
+ program). This can make the program more complex. The advantage of
+ shallow copies is that they consume far less memory than deep
+ copies. It is also much faster to copy a pointer (typically 4 or 8
+ bytes) than to copy string data.
+
+ A Q3StrList that operates on deep copies will, by default, turn on
+ auto-deletion (see setAutoDelete()). Thus, by default Q3StrList
+ will deallocate any string copies it allocates.
+
+ The virtual compareItems() function is reimplemented and does a
+ case-sensitive string comparison. The inSort() function will
+ insert strings in sorted order. In general it is fastest to insert
+ the strings as they come and sort() at the end; inSort() is useful
+ when you just have to add a few extra strings to an already sorted
+ list.
+
+ The Q3StrListIterator class is an iterator for Q3StrList.
+*/
+
+/*!
+ \fn Q3StrList::operator QList<QByteArray>() const
+
+ Automatically converts a Q3StrList into a QList<QByteArray>.
+*/
+
+/*!
+ \fn Q3StrList::Q3StrList( bool deepCopies )
+
+ Constructs an empty list of strings. Will make deep copies of all
+ inserted strings if \a deepCopies is TRUE, or use shallow copies
+ if \a deepCopies is FALSE.
+*/
+
+/*!
+ \fn Q3StrList::Q3StrList(const Q3StrList &list)
+ \fn Q3StrList::Q3StrList(const QList<QByteArray> &list)
+
+ Constructs a copy of \a list.
+*/
+
+/*!
+ \fn Q3StrList::~Q3StrList()
+
+ Destroys the list. All strings are removed.
+*/
+
+/*!
+ \fn Q3StrList& Q3StrList::operator=(const Q3StrList& list)
+ \fn Q3StrList &Q3StrList::operator=(const QList<QByteArray> &list)
+
+ Assigns \a list to this list and returns a reference to this list.
+*/
+
+/*!
+ \class Q3StrIList
+ \brief The Q3StrIList class provides a doubly-linked list of char*
+ with case-insensitive comparison.
+ \compat
+
+ This class is a Q3PtrList\<char\> instance (a list of char*).
+
+ Q3StrIList is identical to Q3StrList except that the virtual
+ compareItems() function is reimplemented to compare strings
+ case-insensitively. The inSort() function inserts strings in a
+ sorted order. In general it is fastest to insert the strings as
+ they come and sort() at the end; inSort() is useful when you just
+ have to add a few extra strings to an already sorted list.
+
+ The Q3StrListIterator class works for Q3StrIList.
+
+ \sa QStringList
+*/
+
+/*!
+ \fn Q3StrIList::Q3StrIList( bool deepCopies )
+
+ Constructs a list of strings. Will make deep copies of all
+ inserted strings if \a deepCopies is TRUE, or use shallow copies
+ if \a deepCopies is FALSE.
+*/
+
+/*!
+ \fn Q3StrIList::~Q3StrIList()
+
+ Destroys the list. All strings are removed.
+*/
+
+/*!
+ \fn int Q3PtrList::compareItems( Q3PtrCollection::Item item1,
+ Q3PtrCollection::Item item2 )
+
+ This virtual function compares two list items.
+
+ Returns:
+ \list
+ \i zero if \a item1 == \a item2
+ \i nonzero if \a item1 != \a item2
+ \endlist
+
+ This function returns \e int rather than \e bool so that
+ reimplementations can return three values and use it to sort by:
+
+ \list
+ \i 0 if \a item1 == \a item2
+ \i \> 0 (positive integer) if \a item1 \> \a item2
+ \i \< 0 (negative integer) if \a item1 \< \a item2
+ \endlist
+
+ inSort() requires that compareItems() is implemented as described
+ here.
+
+ This function should not modify the list because some const
+ functions call compareItems().
+
+ The default implementation compares the pointers.
+*/
+
+/*!
+ \fn QDataStream& Q3PtrList::read( QDataStream& s,
+ Q3PtrCollection::Item& item )
+
+ Reads a list item from the stream \a s and returns a reference to
+ the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3PtrList::write( QDataStream& s,
+ Q3PtrCollection::Item item ) const
+
+ Writes a list item, \a item to the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation does nothing.
+
+ \sa read()
+*/
+
+/*! \fn iterator Q3PtrList::begin()
+\internal
+*/
+/*! \fn const_iterator Q3PtrList::begin() const
+\internal
+*/
+/*! \fn iterator Q3PtrList::end()
+\internal
+*/
+/*! \fn const_iterator Q3PtrList::end() const
+\internal
+*/
+
+/*!
+ \class Q3StrListIterator
+ \brief The Q3StrListIterator class is an iterator for the Q3StrList
+ and Q3StrIList classes.
+ \compat
+
+ This class is a Q3PtrListIterator\<char\> instance. It can traverse
+ the strings in the Q3StrList and Q3StrIList classes.
+*/
+
+
+/*
+ \class Q3PtrListAutoDelete
+ \brief The Q3PtrListAutoDelete class is a template class that provides a list that auto-deletes its data.
+ \compat
+
+ A Q3PtrListAutoDelete is identical to a Q3PtrList with
+ setAutoDelete(TRUE).
+*/
diff --git a/src/qt3support/tools/q3ptrqueue.h b/src/qt3support/tools/q3ptrqueue.h
new file mode 100644
index 0000000..096aef3
--- /dev/null
+++ b/src/qt3support/tools/q3ptrqueue.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PTRQUEUE_H
+#define Q3PTRQUEUE_H
+
+#include <Qt3Support/q3glist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3PtrQueue : protected Q3GList
+{
+public:
+ Q3PtrQueue() {}
+ Q3PtrQueue( const Q3PtrQueue<type> &q ) : Q3GList(q) {}
+ ~Q3PtrQueue() { clear(); }
+ Q3PtrQueue<type>& operator=(const Q3PtrQueue<type> &q)
+ { return (Q3PtrQueue<type>&)Q3GList::operator=(q); }
+ bool autoDelete() const { return Q3PtrCollection::autoDelete(); }
+ void setAutoDelete( bool del ) { Q3PtrCollection::setAutoDelete(del); }
+ uint count() const { return Q3GList::count(); }
+ bool isEmpty() const { return Q3GList::count() == 0; }
+ void enqueue( const type *d ) { Q3GList::append(Item(d)); }
+ type *dequeue() { return (type *)Q3GList::takeFirst();}
+ bool remove() { return Q3GList::removeFirst(); }
+ void clear() { Q3GList::clear(); }
+ type *head() const { return (type *)Q3GList::cfirst(); }
+ operator type *() const { return (type *)Q3GList::cfirst(); }
+ type *current() const { return (type *)Q3GList::cfirst(); }
+
+#ifdef qdoc
+protected:
+ virtual QDataStream& read( QDataStream&, Q3PtrCollection::Item& );
+ virtual QDataStream& write( QDataStream&, Q3PtrCollection::Item ) const;
+#endif
+
+private:
+ void deleteItem( Item d );
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3PtrQueue<void>::deleteItem( Q3PtrCollection::Item )
+{
+}
+#endif
+
+template<class type> inline void Q3PtrQueue<type>::deleteItem( Q3PtrCollection::Item d )
+{
+ if ( del_item ) delete (type *)d;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PTRQUEUE_H
diff --git a/src/qt3support/tools/q3ptrqueue.qdoc b/src/qt3support/tools/q3ptrqueue.qdoc
new file mode 100644
index 0000000..1642ecd
--- /dev/null
+++ b/src/qt3support/tools/q3ptrqueue.qdoc
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3PtrQueue
+ \brief The Q3PtrQueue class is a template class that provides a queue.
+ \compat
+
+ Q3ValueVector can be used as an STL-compatible alternative to this
+ class.
+
+ A template instance Q3PtrQueue\<X\> is a queue that operates on
+ pointers to X (X*).
+
+ A queue is a first in, first out structure. Items are added to the
+ tail of the queue with enqueue() and retrieved from the head with
+ dequeue(). You can peek at the head item without dequeing it using
+ head().
+
+ You can control the queue's deletion policy with setAutoDelete().
+
+ For compatibility with the Q3PtrCollection classes, current() and
+ remove() are provided; both operate on the head().
+
+ \sa Q3PtrList Q3PtrStack
+*/
+
+/*!
+ \fn Q3PtrQueue::Q3PtrQueue ()
+
+ Creates an empty queue with autoDelete() set to FALSE.
+*/
+
+/*!
+ \fn Q3PtrQueue::Q3PtrQueue( const Q3PtrQueue<type>& queue )
+
+ Creates a queue from \a queue.
+
+ Only the pointers are copied; the items are not. The autoDelete()
+ flag is set to FALSE.
+*/
+
+/*!
+ \fn Q3PtrQueue::~Q3PtrQueue()
+
+ Destroys the queue. Items in the queue are deleted if autoDelete()
+ is TRUE.
+*/
+
+/*!
+ \fn Q3PtrQueue<type>& Q3PtrQueue::operator= (const Q3PtrQueue<type>& queue)
+
+ Assigns \a queue to this queue and returns a reference to this
+ queue.
+
+ This queue is first cleared and then each item in \a queue is
+ enqueued to this queue. Only the pointers are copied.
+
+ \warning The autoDelete() flag is not modified. If it is TRUE for
+ both \a queue and this queue, deleting the two lists will cause \e
+ double-deletion of the items.
+*/
+
+/*!
+ \fn bool Q3PtrQueue::isEmpty() const
+
+ Returns TRUE if the queue is empty; otherwise returns FALSE.
+
+ \sa count() dequeue() head()
+*/
+
+/*!
+ \fn void Q3PtrQueue::enqueue (const type* d)
+
+ Adds item \a d to the tail of the queue.
+
+ \sa count() dequeue()
+*/
+
+/*!
+ \fn type* Q3PtrQueue::dequeue ()
+
+ Takes the head item from the queue and returns a pointer to it.
+ Returns 0 if the queue is empty.
+
+ \sa enqueue() count()
+*/
+
+/*!
+ \fn bool Q3PtrQueue::remove()
+
+ Removes the head item from the queue, and returns TRUE if there
+ was an item, i.e. the queue wasn't empty; otherwise returns FALSE.
+
+ The item is deleted if autoDelete() is TRUE.
+
+ \sa head() isEmpty() dequeue()
+*/
+
+/*!
+ \fn void Q3PtrQueue::clear()
+
+ Removes all items from the queue, and deletes them if autoDelete()
+ is TRUE.
+
+ \sa remove()
+*/
+
+/*!
+ \fn uint Q3PtrQueue::count() const
+
+ Returns the number of items in the queue.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn type* Q3PtrQueue::head() const
+
+ Returns a pointer to the head item in the queue. The queue is not
+ changed. Returns 0 if the queue is empty.
+
+ \sa dequeue() isEmpty()
+*/
+
+/*!
+ \fn Q3PtrQueue::operator type*() const
+
+ Returns a pointer to the head item in the queue. The queue is not
+ changed. Returns 0 if the queue is empty.
+
+ \sa dequeue() isEmpty()
+*/
+
+/*!
+ \fn type* Q3PtrQueue::current() const
+
+ Returns a pointer to the head item in the queue. The queue is not
+ changed. Returns 0 if the queue is empty.
+
+ \sa dequeue() isEmpty()
+*/
+
+/*!
+ \fn bool Q3PtrQueue::autoDelete() const
+
+ Returns the setting of the auto-delete option. The default is
+ FALSE.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn void Q3PtrQueue::setAutoDelete( bool enable )
+
+ Sets the queue to auto-delete its contents if \a enable is TRUE
+ and not to delete them if \a enable is FALSE.
+
+ If auto-deleting is turned on, all the items in a queue are
+ deleted when the queue itself is deleted. This can be quite
+ convenient if the queue has the only pointer to the items.
+
+ The default setting is FALSE, for safety. If you turn it on, be
+ careful about copying the queue: you might find yourself with two
+ queues deleting the same items.
+
+ \sa autoDelete()
+*/
+
+/*!
+ \fn QDataStream& Q3PtrQueue::read( QDataStream& s,
+ Q3PtrCollection::Item& item )
+
+ Reads a queue item, \a item, from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3PtrQueue::write( QDataStream& s,
+ Q3PtrCollection::Item item ) const
+
+ Writes a queue item, \a item, to the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation does nothing.
+
+ \sa read()
+*/
diff --git a/src/qt3support/tools/q3ptrstack.h b/src/qt3support/tools/q3ptrstack.h
new file mode 100644
index 0000000..517640e
--- /dev/null
+++ b/src/qt3support/tools/q3ptrstack.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PTRSTACK_H
+#define Q3PTRSTACK_H
+
+#include <Qt3Support/q3glist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3PtrStack : protected Q3GList
+{
+public:
+ Q3PtrStack() { }
+ Q3PtrStack( const Q3PtrStack<type> &s ) : Q3GList( s ) { }
+ ~Q3PtrStack() { clear(); }
+ Q3PtrStack<type> &operator=(const Q3PtrStack<type> &s)
+ { return (Q3PtrStack<type>&)Q3GList::operator=(s); }
+ bool autoDelete() const { return Q3PtrCollection::autoDelete(); }
+ void setAutoDelete( bool del ) { Q3PtrCollection::setAutoDelete(del); }
+ uint count() const { return Q3GList::count(); }
+ bool isEmpty() const { return Q3GList::count() == 0; }
+ void push( const type *d ) { Q3GList::insertAt(0,Item(d)); }
+ type *pop() { return (type *)Q3GList::takeFirst(); }
+ bool remove() { return Q3GList::removeFirst(); }
+ void clear() { Q3GList::clear(); }
+ type *top() const { return (type *)Q3GList::cfirst(); }
+ operator type *() const { return (type *)Q3GList::cfirst(); }
+ type *current() const { return (type *)Q3GList::cfirst(); }
+
+#ifdef qdoc
+protected:
+ virtual QDataStream& read( QDataStream&, Q3PtrCollection::Item& );
+ virtual QDataStream& write( QDataStream&, Q3PtrCollection::Item ) const;
+#endif
+
+private:
+ void deleteItem( Item d );
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3PtrStack<void>::deleteItem( Q3PtrCollection::Item )
+{
+}
+#endif
+
+template<class type> inline void Q3PtrStack<type>::deleteItem( Q3PtrCollection::Item d )
+{
+ if ( del_item ) delete (type *)d;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PTRSTACK_H
diff --git a/src/qt3support/tools/q3ptrstack.qdoc b/src/qt3support/tools/q3ptrstack.qdoc
new file mode 100644
index 0000000..f15f2e7
--- /dev/null
+++ b/src/qt3support/tools/q3ptrstack.qdoc
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3PtrStack
+ \brief The Q3PtrStack class is a template class that provides a stack.
+ \compat
+
+ Q3ValueStack is an STL-compatible alternative to this class.
+
+ Define a template instance Q3PtrStack\<X\> to create a stack that
+ operates on pointers to X, (X*).
+
+ A stack is a last in, first out (LIFO) structure. Items are added
+ to the top of the stack with push() and retrieved from the top
+ with pop(). Use top() to get a reference to the top element
+ without changing the stack.
+
+ You can control the stack's deletion policy with setAutoDelete().
+
+ For compatibility with the Q3PtrCollection classes current() and
+ remove() are provided; they both operate on the top().
+
+ \sa Q3PtrList Q3PtrQueue
+*/
+
+/*!
+ \fn Q3PtrStack::Q3PtrStack ()
+
+ Creates an empty stack.
+*/
+
+/*!
+ \fn Q3PtrStack::Q3PtrStack (const Q3PtrStack<type>& s)
+
+ Creates a stack by making a shallow copy of another stack \a s.
+*/
+
+/*!
+ \fn Q3PtrStack::~Q3PtrStack ()
+
+ Destroys the stack. All items will be deleted if autoDelete() is
+ true.
+*/
+
+/*!
+ \fn Q3PtrStack<type>& Q3PtrStack::operator= (const Q3PtrStack<type>& s)
+
+ Sets the contents of this stack by making a shallow copy of
+ another stack \a s. Elements currently in this stack will be
+ deleted if autoDelete() is true.
+*/
+
+/*!
+ \fn bool Q3PtrStack::isEmpty () const
+
+ Returns true if the stack contains no elements; otherwise returns
+ false.
+*/
+
+/*!
+ \fn void Q3PtrStack::push (const type* d)
+
+ Adds an element \a d to the top of the stack. Last in, first out.
+*/
+
+/*!
+ \fn type* Q3PtrStack::pop ()
+
+ Removes the top item from the stack and returns it. The stack must
+ not be empty.
+*/
+
+/*!
+ \fn bool Q3PtrStack::remove ()
+
+ Removes the top item from the stack and deletes it if autoDelete()
+ is true. Returns true if there was an item to pop; otherwise
+ returns false.
+
+ \sa clear()
+*/
+
+/*!
+ \fn void Q3PtrStack::clear()
+
+ Removes all items from the stack, deleting them if autoDelete() is
+ true.
+
+ \sa remove()
+*/
+
+/*!
+ \fn uint Q3PtrStack::count() const
+
+ Returns the number of items in the stack.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn type* Q3PtrStack::top () const
+
+ Returns a pointer to the top item on the stack (most recently
+ pushed). The stack is not changed. Returns 0 if the stack is
+ empty.
+*/
+
+/*!
+ \fn Q3PtrStack::operator type* ()const
+
+ Returns a pointer to the top item on the stack (most recently
+ pushed). The stack is not changed. Returns 0 if the stack is
+ empty.
+*/
+
+/*!
+ \fn type* Q3PtrStack::current () const
+
+ Returns a pointer to the top item on the stack (most recently
+ pushed). The stack is not changed. Returns 0 if the stack is
+ empty.
+*/
+
+/*!
+ \fn bool Q3PtrStack::autoDelete() const
+
+ The same as Q3PtrCollection::autoDelete(). Returns true if
+ the auto-delete option is set. If the option is set, the
+ stack auto-deletes its contents.
+
+ \sa setAutoDelete()
+*/
+
+/*!
+ \fn void Q3PtrStack::setAutoDelete(bool enable)
+
+ Defines whether this stack auto-deletes its contents. The same as
+ Q3PtrCollection::setAutoDelete(). If \a enable is true, auto-delete
+ is turned on.
+
+ If auto-deleting is turned on, all the items in the stack are
+ deleted when the stack itself is deleted. This is convenient if
+ the stack has the only pointers to the items.
+
+ The default setting is false, for safety. If you turn it on, be
+ careful about copying the stack, or you might find yourself with
+ two stacks deleting the same items.
+
+ Note that the auto-delete setting may also affect other functions in
+ subclasses. For example, a subclass that has a remove() function
+ will remove the item from its data structure, and if auto-delete is
+ enabled, will also delete the item.
+
+ \sa autoDelete()
+*/
+
+/*!
+ \fn QDataStream& Q3PtrStack::read(QDataStream& s, Q3PtrCollection::Item& item)
+
+ Reads a stack item, \a item, from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3PtrStack::write(QDataStream& s,
+ Q3PtrCollection::Item item) const
+
+ Writes a stack item, \a item, to the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation does nothing.
+
+ \sa read()
+*/
diff --git a/src/qt3support/tools/q3ptrvector.h b/src/qt3support/tools/q3ptrvector.h
new file mode 100644
index 0000000..7643a70
--- /dev/null
+++ b/src/qt3support/tools/q3ptrvector.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PTRVECTOR_H
+#define Q3PTRVECTOR_H
+
+#include <Qt3Support/q3gvector.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3PtrVector
+#ifdef qdoc
+ : public Q3PtrCollection
+#else
+ : public Q3GVector
+#endif
+{
+public:
+ Q3PtrVector() { }
+ Q3PtrVector( uint size ) : Q3GVector(size) { }
+ Q3PtrVector( const Q3PtrVector<type> &v ) : Q3GVector( v ) { }
+ ~Q3PtrVector() { clear(); }
+ Q3PtrVector<type> &operator=(const Q3PtrVector<type> &v)
+ { return (Q3PtrVector<type>&)Q3GVector::operator=(v); }
+ bool operator==( const Q3PtrVector<type> &v ) const { return Q3GVector::operator==(v); }
+ type **data() const { return (type **)Q3GVector::data(); }
+ uint size() const { return Q3GVector::size(); }
+ uint count() const { return Q3GVector::count(); }
+ bool isEmpty() const { return Q3GVector::count() == 0; }
+ bool isNull() const { return Q3GVector::size() == 0; }
+ bool resize( uint size ) { return Q3GVector::resize(size); }
+ bool insert( uint i, const type *d){ return Q3GVector::insert(i,(Item)d); }
+ bool remove( uint i ) { return Q3GVector::remove(i); }
+ type *take( uint i ) { return (type *)Q3GVector::take(i); }
+ void clear() { Q3GVector::clear(); }
+ bool fill( const type *d, int size=-1 )
+ { return Q3GVector::fill((Item)d,size);}
+ void sort() { Q3GVector::sort(); }
+ int bsearch( const type *d ) const{ return Q3GVector::bsearch((Item)d); }
+ int findRef( const type *d, uint i=0 ) const
+ { return Q3GVector::findRef((Item)d,i);}
+ int find( const type *d, uint i= 0 ) const
+ { return Q3GVector::find((Item)d,i); }
+ uint containsRef( const type *d ) const
+ { return Q3GVector::containsRef((Item)d); }
+ uint contains( const type *d ) const
+ { return Q3GVector::contains((Item)d); }
+ type *operator[]( int i ) const { return (type *)Q3GVector::at(i); }
+ type *at( uint i ) const { return (type *)Q3GVector::at(i); }
+ void toList( Q3GList *list ) const { Q3GVector::toList(list); }
+
+#ifdef qdoc
+protected:
+ virtual int compareItems( Q3PtrCollection::Item d1, Q3PtrCollection::Item d2 );
+ virtual QDataStream& read( QDataStream &s, Q3PtrCollection::Item &d );
+ virtual QDataStream& write( QDataStream &s, Q3PtrCollection::Item d ) const;
+#endif
+
+private:
+ void deleteItem( Item d );
+};
+
+#if !defined(Q_BROKEN_TEMPLATE_SPECIALIZATION)
+template<> inline void Q3PtrVector<void>::deleteItem( Q3PtrCollection::Item )
+{
+}
+#endif
+
+template<class type> inline void Q3PtrVector<type>::deleteItem( Q3PtrCollection::Item d )
+{
+ if ( del_item ) delete (type *)d;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PTRVECTOR_H
diff --git a/src/qt3support/tools/q3ptrvector.qdoc b/src/qt3support/tools/q3ptrvector.qdoc
new file mode 100644
index 0000000..34b98f5
--- /dev/null
+++ b/src/qt3support/tools/q3ptrvector.qdoc
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3PtrVector
+ \brief The Q3PtrVector class is a template collection class that
+ provides a vector (array).
+ \compat
+
+ Q3ValueVector is an STL-compatible alternative to this class.
+
+ Q3PtrVector is implemented as a template class. Defines a template
+ instance Q3PtrVector\<X\> to create a vector that contains pointers
+ to X (X*).
+
+ A vector is the same as an array. The main difference between
+ Q3PtrVector and Q3MemArray is that Q3PtrVector stores pointers to the
+ elements, whereas Q3MemArray stores the elements themselves (i.e.
+ Q3MemArray is value-based and Q3PtrVector is pointer-based).
+
+ Items are added to the vector using insert() or fill(). Items are
+ removed with remove(). You can get a pointer to an item at a
+ particular index position using at().
+
+ Unless otherwise stated, all functions that remove items from the
+ vector will also delete the element pointed to if \link
+ setAutoDelete() auto-deletion\endlink is enabled. By default,
+ auto-deletion is disabled; see setAutoDelete(). This behavior can
+ be changed in a subclass by reimplementing the virtual function
+ deleteItem().
+
+ Functions that compare items (find() and sort() for example) will
+ do so using the virtual function compareItems(). The default
+ implementation of this function only compares the pointer values.
+ Reimplement compareItems() in a subclass to get searching and
+ sorting based on the item contents. You can perform a linear
+ search for a pointer in the vector using findRef(), or a binary
+ search (of a sorted vector) using bsearch(). You can count the
+ number of times an item appears in the vector with contains() or
+ containsRef().
+
+ \sa Q3MemArray
+*/
+
+/*!
+ \fn Q3PtrVector::Q3PtrVector()
+
+ Constructs a null vector.
+
+ \sa isNull()
+*/
+
+/*!
+ \fn Q3PtrVector::Q3PtrVector(uint size)
+
+ Constructs an vector with room for \a size items. Makes a null
+ vector if \a size == 0.
+
+ All \a size positions in the vector are initialized to 0.
+
+ \sa size(), resize(), isNull()
+*/
+
+/*!
+ \fn Q3PtrVector::Q3PtrVector(const Q3PtrVector<type> &v)
+
+ Constructs a copy of \a v. Only the pointers are copied (i.e.
+ shallow copy).
+*/
+
+/*!
+ \fn Q3PtrVector::~Q3PtrVector()
+
+ Removes all items from the vector, and destroys the vector itself.
+
+ \sa clear()
+*/
+
+/*!
+ \fn Q3PtrVector<type> &Q3PtrVector::operator=(const Q3PtrVector<type> &v)
+
+ Assigns \a v to this vector and returns a reference to this
+ vector.
+
+ This vector is first cleared and then all the items from \a v are
+ copied into the vector. Only the pointers are copied (i.e. shallow
+ copy).
+
+ \sa clear()
+*/
+
+/*!
+ \fn type **Q3PtrVector::data() const
+
+ Returns a pointer to the actual vector data, which is an array of
+ type*.
+
+ The vector is a null vector if data() == 0 (null pointer).
+
+ \sa isNull()
+*/
+
+/*!
+ \fn uint Q3PtrVector::size() const
+
+ Returns the size of the vector, i.e. the number of vector
+ positions. This is also the maximum number of items the vector can
+ hold.
+
+ The vector is a null vector if size() == 0.
+
+ \sa isNull(), resize(), count()
+*/
+
+/*!
+ \fn uint Q3PtrVector::count() const
+
+ Returns the number of items in the vector. The vector is empty if
+ count() == 0.
+
+ \sa isEmpty(), size(), isNull()
+*/
+
+/*!
+ \fn bool Q3PtrVector::isEmpty() const
+
+ Returns true if the vector is empty; otherwise returns false.
+
+ \sa count()
+*/
+
+/*!
+ \fn bool Q3PtrVector::isNull() const
+
+ Returns true if the vector is null; otherwise returns false.
+
+ A null vector has size() == 0 and data() == 0.
+
+ \sa size()
+*/
+
+/*!
+ \fn bool Q3PtrVector::resize(uint size)
+
+ Resizes (expands or shrinks) the vector to \a size elements. The
+ vector becomes a null vector if \a size == 0.
+
+ Any items at position \a size or beyond in the vector are removed.
+ New positions are initialized to 0.
+
+ Returns true if successful, i.e. if the memory was successfully
+ allocated; otherwise returns false.
+
+ \sa size(), isNull()
+*/
+
+/*!
+ \fn bool Q3PtrVector::insert(uint i, const type *d)
+
+ Sets position \a i in the vector to contain the item \a d. \a i
+ must be less than size(). Any previous element in position \a i is
+ removed.
+
+ Returns true if \a i is within range; otherwise returns false.
+
+ \sa at()
+*/
+
+/*!
+ \fn bool Q3PtrVector::remove(uint i)
+
+ Removes the item at position \a i in the vector, if there is one.
+ \a i must be less than size().
+
+ Returns true if \a i is within range; otherwise returns false.
+
+ \sa take(), at()
+*/
+
+/*!
+ \fn type* Q3PtrVector::take(uint i)
+
+ Returns the item at position \a i in the vector, and removes that
+ item from the vector. \a i must be less than size(). If there is
+ no item at position \a i, 0 is returned.
+
+ Unlike remove(), this function does \e not call deleteItem() for
+ the removed item.
+
+ \sa remove(), at()
+*/
+
+/*!
+ \fn void Q3PtrVector::clear()
+
+ Removes all items from the vector, and destroys the vector itself.
+
+ The vector becomes a null vector.
+
+ \sa isNull()
+*/
+
+/*!
+ \fn bool Q3PtrVector::fill(const type *d, int size)
+
+ Inserts item \a d in all positions in the vector. Any existing
+ items are removed. If \a d is 0, the vector becomes empty.
+
+ If \a size >= 0, the vector is first resized to \a size. By
+ default, \a size is -1.
+
+ Returns true if successful, i.e. \a size is the same as the
+ current size, or \a size is larger and the memory has successfully
+ been allocated; otherwise returns false.
+
+ \sa resize(), insert(), isEmpty()
+*/
+
+/*!
+ \fn void Q3PtrVector::sort()
+
+ Sorts the items in ascending order. Any empty positions will be
+ put last.
+
+ Compares items using the virtual function compareItems().
+
+ \sa bsearch()
+*/
+
+/*!
+ \fn int Q3PtrVector::bsearch(const type* d) const
+
+ In a sorted array, finds the first occurrence of \a d using a
+ binary search. For a sorted array, this is generally much faster
+ than find(), which performs a linear search.
+
+ Returns the position of \a d, or -1 if \a d could not be found. \a
+ d must not be 0.
+
+ Compares items using the virtual function compareItems().
+
+ \sa sort(), find()
+*/
+
+
+/*!
+ \fn int Q3PtrVector::findRef(const type *d, uint i) const
+
+ Finds the first occurrence of the item pointer \a d in the vector
+ using a linear search. The search starts at position \a i, which
+ must be less than size(). \a i is by default 0; i.e. the search
+ starts at the start of the vector.
+
+ Returns the position of \a d, or -1 if \a d could not be found.
+
+ This function does \e not use compareItems() to compare items.
+
+ Use the much faster bsearch() to search a sorted vector.
+
+ \sa find(), bsearch()
+*/
+
+/*!
+ \fn int Q3PtrVector::find(const type *d, uint i) const
+
+ Finds the first occurrence of item \a d in the vector using a
+ linear search. The search starts at position \a i, which must be
+ less than size(). \a i is by default 0; i.e. the search starts at
+ the start of the vector.
+
+ Returns the position of \a d, or -1 if \a d could not be found.
+
+ Compares items using the virtual function compareItems().
+
+ Use the much faster bsearch() to search a sorted vector.
+
+ \sa findRef(), bsearch()
+*/
+
+
+/*!
+ \fn uint Q3PtrVector::containsRef(const type *d) const
+
+ Returns the number of occurrences of the item pointer \a d in the
+ vector.
+
+ This function does \e not use compareItems() to compare items.
+
+ \sa findRef()
+*/
+
+/*!
+ \fn uint Q3PtrVector::contains(const type *d) const
+
+ Returns the number of occurrences of item \a d in the vector.
+
+ Compares items using the virtual function compareItems().
+
+ \sa containsRef()
+*/
+
+/*!
+ \fn type *Q3PtrVector::operator[](int i) const
+
+ Returns the item at position \a i, or 0 if there is no item at
+ that position. \a i must be less than size().
+
+ Equivalent to at(\a i).
+
+ \sa at()
+*/
+
+/*!
+ \fn type *Q3PtrVector::at(uint i) const
+
+ Returns the item at position \a i, or 0 if there is no item at
+ that position. \a i must be less than size().
+*/
+
+
+/*!
+ \fn void Q3PtrVector::toList(Q3GList *list) const
+
+ \internal
+
+ Copies all items in this vector to the list \a list. \a list is
+ first cleared and then all items are appended to \a list.
+
+ \sa Q3PtrList, Q3PtrStack, Q3PtrQueue
+*/
+
+/*!
+ \fn int Q3PtrVector::compareItems(Q3PtrCollection::Item d1,
+ Q3PtrCollection::Item d2)
+
+ This virtual function compares two list items.
+
+ Returns:
+ \list
+ \i zero if \a d1 == \a d2
+ \i nonzero if \a d1 != \a d2
+ \endlist
+
+ This function returns \e int rather than \e bool so that
+ reimplementations can return one of three values and use it to
+ sort by:
+ \list
+ \i 0 if \a d1 == \a d2
+ \i \> 0 (positive integer) if \a d1 \> \a d2
+ \i \< 0 (negative integer) if \a d1 \< \a d2
+ \endlist
+
+ The sort() and bsearch() functions require compareItems() to be
+ implemented as described here.
+
+ This function should not modify the vector because some const
+ functions call compareItems().
+*/
+
+/*!
+ \fn QDataStream& Q3PtrVector::read(QDataStream &s,
+ Q3PtrCollection::Item &item)
+
+ Reads a vector item, \a item, from the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation sets \a item to 0.
+
+ \sa write()
+*/
+
+/*!
+ \fn QDataStream& Q3PtrVector::write(QDataStream &s,
+ Q3PtrCollection::Item item) const
+
+ Writes a vector item, \a item, to the stream \a s and returns a
+ reference to the stream.
+
+ The default implementation does nothing.
+
+ \sa read()
+*/
+
+/*!
+ \fn bool Q3PtrVector::operator==(const Q3PtrVector<type> &v) const
+
+ Returns true if this vector and \a v are equal; otherwise returns
+ false.
+*/
diff --git a/src/qt3support/tools/q3semaphore.cpp b/src/qt3support/tools/q3semaphore.cpp
new file mode 100644
index 0000000..c318d72
--- /dev/null
+++ b/src/qt3support/tools/q3semaphore.cpp
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3semaphore.h"
+
+#include "qmutex.h"
+#include "qwaitcondition.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3Semaphore
+ \threadsafe
+ \brief The Q3Semaphore class provides a robust integer semaphore.
+
+ \compat
+
+ A Q3Semaphore can be used to serialize thread execution, in a
+ similar way to a QMutex. A semaphore differs from a mutex, in
+ that a semaphore can be accessed by more than one thread at a
+ time.
+
+ For example, suppose we have an application that stores data in a
+ large tree structure. The application creates 10 threads
+ (commonly called a thread pool) to perform searches on the tree.
+ When the application searches the tree for some piece of data, it
+ uses one thread per base node to do the searching. A semaphore
+ could be used to make sure that two threads don't try to search
+ the same branch of the tree at the same time.
+
+ A non-computing example of a semaphore would be dining at a
+ restaurant. A semaphore is initialized to have a maximum count
+ equal to the number of chairs in the restaurant. As people
+ arrive, they want a seat. As seats are filled, the semaphore is
+ accessed, once per person. As people leave, the access is
+ released, allowing more people to enter. If a party of 10 people
+ want to be seated, but there are only 9 seats, those 10 people
+ will wait, but a party of 4 people would be seated (taking the
+ available seats to 5, making the party of 10 people wait longer).
+
+ When a semaphore is created it is given a number which is the
+ maximum number of concurrent accesses it will permit. This amount
+ may be changed using operator++(), operator--(), operator+=() and
+ operator-=(). The number of accesses allowed is retrieved with
+ available(), and the total number with total(). Note that the
+ incrementing functions will block if there aren't enough available
+ accesses. Use tryAccess() if you want to acquire accesses without
+ blocking.
+*/
+
+#ifdef max
+#undef max
+#endif
+
+class Q3SemaphorePrivate {
+public:
+ Q3SemaphorePrivate(int);
+
+ QMutex mutex;
+ QWaitCondition cond;
+
+ int value, max;
+};
+
+
+Q3SemaphorePrivate::Q3SemaphorePrivate(int m)
+ : value(0), max(m)
+{
+}
+
+
+/*!
+ Creates a new semaphore. The semaphore can be concurrently
+ accessed at most \a maxcount times.
+*/
+Q3Semaphore::Q3Semaphore(int maxcount)
+{
+ d = new Q3SemaphorePrivate(maxcount);
+}
+
+
+/*!
+ Destroys the semaphore.
+
+ \warning If you destroy a semaphore that has accesses in use the
+ resultant behavior is undefined.
+*/
+Q3Semaphore::~Q3Semaphore()
+{
+ delete d;
+}
+
+
+/*!
+ Postfix ++ operator.
+
+ Try to get access to the semaphore. If \l available() == 0, this
+ call will block until it can get access, i.e. until available() \>
+ 0.
+*/
+int Q3Semaphore::operator++(int)
+{
+ QMutexLocker locker(&d->mutex);
+ while (d->value >= d->max)
+ d->cond.wait(locker.mutex());
+
+ ++d->value;
+ if (d->value > d->max)
+ d->value = d->max;
+
+ return d->value;
+}
+
+
+/*!
+ Postfix -- operator.
+
+ Release access of the semaphore. This wakes all threads waiting
+ for access to the semaphore.
+*/
+int Q3Semaphore::operator--(int)
+{
+ QMutexLocker locker(&d->mutex);
+
+ --d->value;
+ if (d->value < 0)
+ d->value = 0;
+
+ d->cond.wakeAll();
+
+ return d->value;
+}
+
+
+/*!
+ Try to get access to the semaphore. If \l available() \< \a n, this
+ call will block until it can get all the accesses it wants, i.e.
+ until available() \>= \a n.
+*/
+int Q3Semaphore::operator+=(int n)
+{
+ QMutexLocker locker(&d->mutex);
+
+ if (n < 0 || n > d->max) {
+ qWarning("Q3Semaphore::operator+=: parameter %d out of range", n);
+ n = n < 0 ? 0 : d->max;
+ }
+
+ while (d->value + n > d->max)
+ d->cond.wait(locker.mutex());
+
+ d->value += n;
+
+ return d->value;
+}
+
+
+/*!
+ Release \a n accesses to the semaphore.
+*/
+int Q3Semaphore::operator-=(int n)
+{
+ QMutexLocker locker(&d->mutex);
+
+ if (n < 0 || n > d->value) {
+ qWarning("Q3Semaphore::operator-=: parameter %d out of range", n);
+ n = n < 0 ? 0 : d->value;
+ }
+
+ d->value -= n;
+ d->cond.wakeAll();
+
+ return d->value;
+}
+
+
+/*!
+ Returns the number of accesses currently available to the
+ semaphore.
+*/
+int Q3Semaphore::available() const
+{
+ QMutexLocker locker(&d->mutex);
+ return d->max - d->value;
+}
+
+
+/*!
+ Returns the total number of accesses to the semaphore.
+*/
+int Q3Semaphore::total() const
+{
+ QMutexLocker locker(&d->mutex);
+ return d->max;
+}
+
+
+/*!
+ Try to get access to the semaphore. If \l available() \< \a n, this
+ function will return false immediately. If \l available() \>= \a n,
+ this function will take \a n accesses and return true. This
+ function does \e not block.
+*/
+bool Q3Semaphore::tryAccess(int n)
+{
+ QMutexLocker locker(&d->mutex);
+
+ if (d->value + n > d->max)
+ return false;
+
+ d->value += n;
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3semaphore.h b/src/qt3support/tools/q3semaphore.h
new file mode 100644
index 0000000..f89bcf5
--- /dev/null
+++ b/src/qt3support/tools/q3semaphore.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SEMAPHORE_H
+#define Q3SEMAPHORE_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3SemaphorePrivate;
+
+class Q_COMPAT_EXPORT Q3Semaphore
+{
+public:
+ Q3Semaphore(int);
+ virtual ~Q3Semaphore();
+
+ int available() const;
+ int total() const;
+
+ // postfix operators
+ int operator++(int);
+ int operator--(int);
+
+ int operator+=(int);
+ int operator-=(int);
+
+ bool tryAccess(int);
+
+private:
+ Q_DISABLE_COPY(Q3Semaphore)
+
+ Q3SemaphorePrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SEMAPHORE_H
diff --git a/src/qt3support/tools/q3shared.cpp b/src/qt3support/tools/q3shared.cpp
new file mode 100644
index 0000000..8611320
--- /dev/null
+++ b/src/qt3support/tools/q3shared.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3shared.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3Shared
+ \brief The Q3Shared class is used internally for implementing shared classes.
+ \compat
+
+ Use QSharedData and QSharedDataPointer instead.
+*/
+
+/*!
+ \fn Q3Shared::Q3Shared()
+
+ Constructs a Q3Shared object with a reference count of 1.
+*/
+
+/*!
+ \fn void Q3Shared::ref()
+
+ Increments the reference count.
+*/
+
+/*!
+ \fn bool Q3Shared::deref()
+ Decrements the reference count and returns true if
+ any references remain.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3shared.h b/src/qt3support/tools/q3shared.h
new file mode 100644
index 0000000..ec60aed
--- /dev/null
+++ b/src/qt3support/tools/q3shared.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SHARED_H
+#define Q3SHARED_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+struct Q_COMPAT_EXPORT Q3Shared
+{
+ Q3Shared() : count( 1 ) { }
+ void ref() { count++; }
+ bool deref() { return !--count; }
+ uint count;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SHARED_H
diff --git a/src/qt3support/tools/q3signal.cpp b/src/qt3support/tools/q3signal.cpp
new file mode 100644
index 0000000..85e9070
--- /dev/null
+++ b/src/qt3support/tools/q3signal.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3signal.h"
+#include "qmetaobject.h"
+#include "qpointer.h"
+#include "q3cstring.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3Signal
+ \brief The Q3Signal class can be used to send signals for classes
+ that don't inherit QObject.
+
+ \compat
+
+ If you want to send signals from a class that does not inherit
+ QObject, you can create an internal Q3Signal object to emit the
+ signal. You must also provide a function that connects the signal
+ to an outside object slot. This is how we used to implement
+ signals in Qt 3's QMenuData class, which was not a QObject. In Qt
+ 4, menus contain actions, which are QObjects.
+
+ In general, we recommend inheriting QObject instead. QObject
+ provides much more functionality.
+
+ You can set a single QVariant parameter for the signal with
+ setValue().
+
+ Note that QObject is a \e private base class of Q3Signal, i.e. you
+ cannot call any QObject member functions from a Q3Signal object.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_tools_q3signal.cpp 0
+*/
+
+/*!
+ Constructs a signal object called \a name, with the parent object
+ \a parent. These arguments are passed directly to QObject.
+*/
+
+Q3Signal::Q3Signal(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+#ifndef QT_NO_VARIANT
+ val = 0;
+#endif
+}
+
+/*!
+ Destroys the signal. All connections are removed, as is the case
+ with all QObjects.
+*/
+Q3Signal::~Q3Signal()
+{
+}
+#ifndef QT_NO_VARIANT
+// Returns true if it matches ".+(.*int.*"
+static inline bool intSignature(const char *member)
+{
+ Q3CString s(member);
+ int p = s.find('(');
+ return p > 0 && p < s.findRev("int");
+}
+#endif
+/*!
+ Connects the signal to \a member in object \a receiver.
+ Returns true if the connection is successful.
+
+ \sa disconnect(), QObject::connect()
+*/
+
+bool Q3Signal::connect(const QObject *receiver, const char *member)
+{
+#ifndef QT_NO_VARIANT
+ if (intSignature(member))
+#endif
+ return QObject::connect((QObject *)this, SIGNAL(intSignal(int)), receiver, member);
+#ifndef QT_NO_VARIANT
+ return QObject::connect((QObject *)this, SIGNAL(signal(QVariant)),
+ receiver, member);
+#endif
+}
+
+/*!
+ Disonnects the signal from \a member in object \a receiver.
+ Returns true if the connection existed and the disconnect
+ was successful.
+
+ \sa connect(), QObject::disconnect()
+*/
+
+bool Q3Signal::disconnect(const QObject *receiver, const char *member)
+{
+ if (!member)
+ return QObject::disconnect((QObject *)this, 0, receiver, member);
+#ifndef QT_NO_VARIANT
+ if (intSignature(member))
+#endif
+ return QObject::disconnect((QObject *)this, SIGNAL(intSignal(int)), receiver, member);
+#ifndef QT_NO_VARIANT
+ return QObject::disconnect((QObject *)this, SIGNAL(signal(QVariant)),
+ receiver, member);
+#endif
+}
+
+
+/*!
+ \fn bool Q3Signal::isBlocked() const
+ \obsolete
+ Returns true if the signal is blocked, or false if it is not blocked.
+
+ The signal is not blocked by default.
+
+ \sa block(), QObject::signalsBlocked()
+*/
+
+/*!
+ \fn void Q3Signal::block(bool b)
+ \obsolete
+ Blocks the signal if \a b is true, or unblocks the signal if \a b is false.
+
+ An activated signal disappears into hyperspace if it is blocked.
+
+ \sa isBlocked(), activate(), QObject::blockSignals()
+*/
+
+
+/*!
+ \fn void Q3Signal::activate()
+
+ Emits the signal. If the platform supports QVariant and a
+ parameter has been set with setValue(), this value is passed in
+ the signal.
+*/
+void Q3Signal::activate()
+{
+#ifndef QT_NO_VARIANT
+ /* Create this Q3GuardedPtr on this, if we get destroyed after the intSignal (but before the variant signal)
+ we cannot just emit the signal (because val has been destroyed already) */
+ QPointer<Q3Signal> me = this;
+ if(me)
+ emit intSignal(val.toInt());
+ if(me)
+ emit signal(val);
+#else
+ emit intSignal(0);
+#endif
+}
+
+#ifndef QT_NO_VARIANT
+/*!
+ Sets the signal's parameter to \a value
+*/
+void Q3Signal::setValue(const QVariant &value)
+{
+ val = value;
+}
+
+/*!
+ Returns the signal's parameter
+*/
+QVariant Q3Signal::value() const
+{
+ return val;
+}
+/*! \fn void Q3Signal::signal(const QVariant &)
+ \internal
+*/
+/*! \fn void Q3Signal::intSignal(int)
+ \internal
+*/
+
+/*! \obsolete */
+void Q3Signal::setParameter(int value)
+{
+ val = value;
+}
+
+/*! \obsolete */
+int Q3Signal::parameter() const
+{
+ return val.toInt();
+}
+#endif //QT_NO_VARIANT
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/tools/q3signal.h b/src/qt3support/tools/q3signal.h
new file mode 100644
index 0000000..5503659
--- /dev/null
+++ b/src/qt3support/tools/q3signal.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SIGNAL_H
+#define Q3SIGNAL_H
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3Signal : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q3Signal(QObject *parent=0, const char *name=0);
+ ~Q3Signal();
+
+ bool connect(const QObject *receiver, const char *member);
+ bool disconnect(const QObject *receiver, const char *member=0);
+
+ void activate();
+
+ bool isBlocked() const { return QObject::signalsBlocked(); }
+ void block(bool b) { QObject::blockSignals(b); }
+#ifndef QT_NO_VARIANT
+ void setParameter(int value);
+ int parameter() const;
+#endif
+
+#ifndef QT_NO_VARIANT
+ void setValue(const QVariant &value);
+ QVariant value() const;
+#endif
+Q_SIGNALS:
+#ifndef QT_NO_VARIANT
+ void signal(const QVariant&);
+#endif
+ void intSignal(int);
+
+private:
+ Q_DISABLE_COPY(Q3Signal)
+
+#ifndef QT_NO_VARIANT
+ QVariant val;
+#endif
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SIGNAL_H
diff --git a/src/qt3support/tools/q3sortedlist.h b/src/qt3support/tools/q3sortedlist.h
new file mode 100644
index 0000000..1145fd2
--- /dev/null
+++ b/src/qt3support/tools/q3sortedlist.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SORTEDLIST_H
+#define Q3SORTEDLIST_H
+
+#include <Qt3Support/q3ptrlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class type>
+class Q3SortedList : public Q3PtrList<type>
+{
+public:
+ Q3SortedList() {}
+ Q3SortedList( const Q3SortedList<type> &l ) : Q3PtrList<type>(l) {}
+ ~Q3SortedList() { this->clear(); }
+ Q3SortedList<type> &operator=(const Q3SortedList<type> &l)
+ { return (Q3SortedList<type>&)Q3PtrList<type>::operator=(l); }
+
+ virtual int compareItems( Q3PtrCollection::Item s1, Q3PtrCollection::Item s2 )
+ { if ( *((type*)s1) == *((type*)s2) ) return 0; return ( *((type*)s1) < *((type*)s2) ? -1 : 1 ); }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SORTEDLIST_H
diff --git a/src/qt3support/tools/q3strlist.h b/src/qt3support/tools/q3strlist.h
new file mode 100644
index 0000000..69418f8
--- /dev/null
+++ b/src/qt3support/tools/q3strlist.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3STRLIST_H
+#define Q3STRLIST_H
+
+#include <QtCore/qstring.h>
+#include <Qt3Support/q3ptrlist.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#if defined(qdoc)
+class Q3StrListIterator : public Q3PtrListIterator<char>
+{
+};
+#else
+typedef Q3PtrListIterator<char> Q3StrListIterator;
+#endif
+
+class Q_COMPAT_EXPORT Q3StrList : public Q3PtrList<char>
+{
+public:
+ Q3StrList( bool deepCopies=true ) { dc = deepCopies; del_item = deepCopies; }
+ Q3StrList( const Q3StrList & );
+ ~Q3StrList() { clear(); }
+ Q3StrList& operator=( const Q3StrList & );
+ Q3StrList(const QList<QByteArray> &list) {
+ for (int i = 0; i < list.size(); ++i)
+ append(list.at(i).constData());
+ }
+
+ Q3StrList &operator =(const QList<QByteArray> &list) {
+ clear();
+ for (int i = 0; i < list.size(); ++i)
+ append(list.at(i).constData());
+ return *this;
+ }
+
+ operator QList<QByteArray>() const {
+ QList<QByteArray> list;
+ for (Q3PtrListStdIterator<char> it = begin(); it != end(); ++it)
+ list.append(QByteArray(*it));
+ return list;
+ }
+
+private:
+ Q3PtrCollection::Item newItem( Q3PtrCollection::Item d ) { return dc ? qstrdup( (const char*)d ) : d; }
+ void deleteItem( Q3PtrCollection::Item d ) { if ( del_item ) delete[] (char*)d; }
+ int compareItems( Q3PtrCollection::Item s1, Q3PtrCollection::Item s2 ) { return qstrcmp((const char*)s1,
+ (const char*)s2); }
+#ifndef QT_NO_DATASTREAM
+ QDataStream &read( QDataStream &s, Q3PtrCollection::Item &d )
+ { s >> (char *&)d; return s; }
+ QDataStream &write( QDataStream &s, Q3PtrCollection::Item d ) const
+ { return s << (const char *)d; }
+#endif
+ bool dc;
+};
+
+
+class Q_COMPAT_EXPORT Q3StrIList : public Q3StrList // case insensitive string list
+{
+public:
+ Q3StrIList( bool deepCopies=true ) : Q3StrList( deepCopies ) {}
+ ~Q3StrIList() { clear(); }
+private:
+ int compareItems( Q3PtrCollection::Item s1, Q3PtrCollection::Item s2 )
+ { return qstricmp((const char*)s1,
+ (const char*)s2); }
+};
+
+
+inline Q3StrList & Q3StrList::operator=( const Q3StrList &strList )
+{
+ clear();
+ dc = strList.dc;
+ del_item = dc;
+ Q3PtrList<char>::operator=( strList );
+ return *this;
+}
+
+inline Q3StrList::Q3StrList( const Q3StrList &strList )
+ : Q3PtrList<char>( strList )
+{
+ dc = false;
+ operator=( strList );
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3STRLIST_H
diff --git a/src/qt3support/tools/q3strvec.h b/src/qt3support/tools/q3strvec.h
new file mode 100644
index 0000000..eb11e2f
--- /dev/null
+++ b/src/qt3support/tools/q3strvec.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3STRVEC_H
+#define Q3STRVEC_H
+
+#include <QtCore/qstring.h>
+#include <Qt3Support/q3ptrvector.h>
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3StrVec : public Q3PtrVector<char>
+{
+public:
+ Q3StrVec() { dc = true; }
+ Q3StrVec( uint size, bool deepc = true ) : Q3PtrVector<char>(size) {dc=deepc;}
+ ~Q3StrVec() { clear(); }
+private:
+ Item newItem( Item d ) { return dc ? qstrdup( (const char*)d ) : d; }
+ void deleteItem( Item d ) { if ( dc ) delete[] (char*)d; }
+ int compareItems( Item s1, Item s2 )
+ { return qstrcmp((const char*)s1,
+ (const char*)s2); }
+#ifndef QT_NO_DATASTREAM
+ QDataStream &read( QDataStream &s, Item &d )
+ { s >> (char *&)d; return s; }
+ QDataStream &write( QDataStream &s, Item d ) const
+ { return s << (const char*)d; }
+#endif
+ bool dc;
+};
+
+
+class Q3StrIVec : public Q3StrVec // case insensitive string vec
+{
+public:
+ Q3StrIVec() {}
+ Q3StrIVec( uint size, bool dc = true ) : Q3StrVec( size, dc ) {}
+ ~Q3StrIVec() { clear(); }
+private:
+ int compareItems( Item s1, Item s2 )
+ { return qstricmp((const char*)s1,
+ (const char*)s2); }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3STRVEC_H
diff --git a/src/qt3support/tools/q3tl.h b/src/qt3support/tools/q3tl.h
new file mode 100644
index 0000000..c856cbf
--- /dev/null
+++ b/src/qt3support/tools/q3tl.h
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TL_H
+#define Q3TL_H
+
+#include <QtCore/qalgorithms.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template <typename T, typename LessThan>
+Q_OUTOFLINE_TEMPLATE void qHeapSortPushDown(T *heap, int first, int last, LessThan lessThan)
+{
+ int r = first;
+ while (r <= last / 2) {
+ if (last == 2 * r) {
+ // node r has only one child
+ if (lessThan(heap[2 * r], heap[r]))
+ qSwap(heap[r], heap[2 * r]);
+ r = last;
+ } else {
+ // node r has two children
+ if (lessThan(heap[2 * r], heap[r]) && !lessThan(heap[2 * r + 1], heap[2 * r])) {
+ // swap with left child
+ qSwap(heap[r], heap[2 * r]);
+ r *= 2;
+ } else if (lessThan(heap[2 * r + 1], heap[r])
+ && lessThan(heap[2 * r + 1], heap[2 * r])) {
+ // swap with right child
+ qSwap(heap[r], heap[2 * r + 1]);
+ r = 2 * r + 1;
+ } else {
+ r = last;
+ }
+ }
+ }
+}
+
+template <typename BiIterator, typename T, typename LessThan>
+Q_OUTOFLINE_TEMPLATE void qHeapSortHelper(BiIterator begin, BiIterator end, const T & /* dummy */, LessThan lessThan)
+{
+ BiIterator it = begin;
+ uint n = 0;
+ while (it != end) {
+ ++n;
+ ++it;
+ }
+ if (n == 0)
+ return;
+
+ // Create the heap
+ BiIterator insert = begin;
+ T *realheap = new T[n];
+ T *heap = realheap - 1;
+ int size = 0;
+ for(; insert != end; ++insert) {
+ heap[++size] = *insert;
+ int i = size;
+ while (i > 1 && lessThan(heap[i], heap[i / 2])) {
+ qSwap(heap[i], heap[i / 2]);
+ i /= 2;
+ }
+ }
+
+ // Now do the sorting
+ for (int i = n; i > 0; i--) {
+ *begin++ = heap[1];
+ if (i > 1) {
+ heap[1] = heap[i];
+ qHeapSortPushDown(heap, 1, i - 1, lessThan);
+ }
+ }
+
+ delete[] realheap;
+}
+
+template <typename BiIterator, typename T>
+inline void qHeapSortHelper(BiIterator begin, BiIterator end, const T &dummy)
+{
+ qHeapSortHelper(begin, end, dummy, qLess<T>());
+}
+
+template <typename BiIterator, typename LessThan>
+inline void qHeapSort(BiIterator begin, BiIterator end, LessThan lessThan)
+{
+ if (begin != end)
+ qHeapSortHelper(begin, end, *begin, lessThan);
+}
+
+template <typename BiIterator>
+inline void qHeapSort(BiIterator begin, BiIterator end)
+{
+ if (begin != end)
+ qHeapSortHelper(begin, end, *begin);
+}
+
+template <typename Container>
+inline void qHeapSort(Container &c)
+{
+#ifdef Q_CC_BOR
+ // Work around Borland 5.5 optimizer bug
+ c.detach();
+#endif
+ if (!c.empty())
+ qHeapSortHelper(c.begin(), c.end(), *c.begin());
+}
+
+
+template <typename BiIterator, typename LessThan>
+void qBubbleSort(BiIterator begin, BiIterator end, LessThan lessThan)
+{
+ // Goto last element;
+ BiIterator last = end;
+
+ // empty list
+ if (begin == end)
+ return;
+
+ --last;
+ // only one element ?
+ if (last == begin)
+ return;
+
+ // So we have at least two elements in here
+ while (begin != last) {
+ bool swapped = false;
+ BiIterator swapPos = begin;
+ BiIterator x = end;
+ BiIterator y = x;
+ y--;
+ do {
+ --x;
+ --y;
+ if (lessThan(*x, *y)) {
+ swapped = true;
+ qSwap(*x, *y);
+ swapPos = y;
+ }
+ } while (y != begin);
+ if (!swapped)
+ return;
+ begin = swapPos;
+ ++begin;
+ }
+}
+
+template <typename BiIterator, typename T>
+void qBubbleSortHelper(BiIterator begin, BiIterator end, T)
+{
+ qBubbleSort(begin, end, qLess<T>());
+}
+
+template <typename BiIterator>
+void qBubbleSort(BiIterator begin, BiIterator end)
+{
+ if (begin != end)
+ qBubbleSortHelper(begin, end, *begin);
+}
+
+template <typename Container>
+inline void qBubbleSort(Container &c)
+{
+ qBubbleSort(c.begin(), c.end());
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TL_H
diff --git a/src/qt3support/tools/q3valuelist.h b/src/qt3support/tools/q3valuelist.h
new file mode 100644
index 0000000..361559e
--- /dev/null
+++ b/src/qt3support/tools/q3valuelist.h
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3VALUELIST_H
+#define Q3VALUELIST_H
+
+#include <QtCore/qalgorithms.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qlinkedlist.h>
+#include <QtCore/qlist.h>
+
+#ifndef QT_NO_STL
+#include <iterator>
+#include <list>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template <typename T>
+class Q3ValueListIterator : public QLinkedList<T>::iterator
+{
+public:
+ inline Q3ValueListIterator() :
+ QLinkedList<T>::iterator() {}
+ inline Q3ValueListIterator(const Q3ValueListIterator &o) :
+ QLinkedList<T>::iterator(o) {}
+ inline Q3ValueListIterator(const typename QLinkedList<T>::iterator &o) :
+ QLinkedList<T>::iterator(o) {}
+};
+
+template <typename T>
+class Q3ValueListConstIterator : public QLinkedList<T>::const_iterator
+{
+public:
+ inline Q3ValueListConstIterator() {}
+ inline Q3ValueListConstIterator(const Q3ValueListConstIterator &o) :
+ QLinkedList<T>::const_iterator(o) {}
+ inline Q3ValueListConstIterator(const typename QLinkedList<T>::const_iterator &o) :
+ QLinkedList<T>::const_iterator(o) {}
+ inline Q3ValueListConstIterator(const typename QLinkedList<T>::iterator &o) :
+ QLinkedList<T>::const_iterator(o) {}
+};
+
+template <typename T>
+class Q3ValueList : public QLinkedList<T>
+{
+public:
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+#ifndef QT_NO_STL
+ typedef ptrdiff_t difference_type;
+#else
+ typedef int difference_type;
+#endif
+
+ typedef Q3ValueListIterator<T> Iterator;
+ typedef Q3ValueListConstIterator<T> ConstIterator;
+ typedef Q3ValueListIterator<T> iterator;
+ typedef Q3ValueListConstIterator<T> const_iterator;
+ typedef typename QLinkedList<T>::size_type size_type;
+
+ /**
+ * API
+ */
+ Q3ValueList() {}
+ Q3ValueList(const Q3ValueList<T>& l) : QLinkedList<T>(l) {}
+ Q3ValueList(const QLinkedList<T>& l) : QLinkedList<T>(l) {}
+ Q3ValueList(const QList<T>& l)
+ {
+ for (int i = 0; i < l.size(); ++i) append(l.at(i));
+ }
+#ifndef QT_NO_STL
+ Q3ValueList(const std::list<T>& l)
+ {
+ qCopy(l.begin(), l.end(), std::back_inserter(*this));
+ }
+#endif
+ ~Q3ValueList() {}
+
+ Q3ValueList<T>& operator= (const Q3ValueList<T>& l)
+ {
+ QLinkedList<T>::operator=(l);
+ return *this;
+ }
+ Q3ValueList<T>& operator= (const QList<T>& l)
+ {
+ this->clear();
+ for (int i = 0; i < l.size(); ++i) append(l.at(i));
+ return *this;
+ }
+#ifndef QT_NO_STL
+ Q3ValueList<T>& operator= (const std::list<T>& l)
+ {
+ this->detach();
+ qCopy(l.begin(), l.end(), std::back_inserter(*this));
+ return *this;
+ }
+ bool operator== (const std::list<T>& l) const
+ {
+ if (this->size() != l.size())
+ return false;
+ typename Q3ValueList<T>::const_iterator it2 = this->begin();
+#if !defined(Q_CC_MIPS)
+ typename
+#endif
+ std::list<T>::const_iterator it = l.begin();
+ for (; it2 != this->end(); ++it2, ++it)
+ if (!((*it2) == (*it)))
+ return false;
+ return true;
+ }
+#endif
+ bool operator== (const Q3ValueList<T>& l) const { return QLinkedList<T>::operator==(l); }
+ bool operator!= (const Q3ValueList<T>& l) const { return QLinkedList<T>::operator!=(l); }
+
+ operator QList<T>() const {
+ QList<T> list;
+ for (typename Q3ValueList<T>::const_iterator it = QLinkedList<T>::constBegin();
+ it != QLinkedList<T>::constEnd(); ++it)
+ list.append(*it);
+ return list;
+ }
+
+ inline Q3ValueList<T>& operator<< (const T& x) { append(x); return *this; }
+
+ void insert(typename Q3ValueList<T>::Iterator pos,
+ typename Q3ValueList<T>::size_type n,
+ const T& x);
+
+ typename Q3ValueList<T>::Iterator insert(typename Q3ValueList<T>::Iterator pos,
+ const T& x)
+ { return QLinkedList<T>::insert(pos, x); }
+ typename Q3ValueList<T>::Iterator remove(typename Q3ValueList<T>::Iterator pos)
+ { return QLinkedList<T>::erase(pos); }
+ int remove(const T &value)
+ { return QLinkedList<T>::removeAll(value); }
+
+ inline Q3ValueList<T> operator+ (const Q3ValueList<T>& l) const
+ { return static_cast<Q3ValueList<T> >(QLinkedList<T>::operator+(l)); }
+ inline Q3ValueList<T>& operator+= (const Q3ValueList<T>& l)
+ { QLinkedList<T>::operator+=(l); return *this; }
+
+ typename Q3ValueList<T>::Iterator fromLast()
+ { return (this->isEmpty() ? this->end() : --this->end()); }
+ typename Q3ValueList<T>::ConstIterator fromLast() const
+ { return (this->isEmpty() ? this->end() : --this->end()); }
+
+ typename Q3ValueList<T>::Iterator append(const T& x)
+ { QLinkedList<T>::append(x); return --this->end(); }
+ typename Q3ValueList<T>::Iterator prepend(const T& x)
+ { QLinkedList<T>::prepend(x); return this->begin(); }
+
+ typename Q3ValueList<T>::Iterator at(typename Q3ValueList<T>::size_type i)
+ { Q_ASSERT(i < this->size()); this->detach(); return this->begin()+i; }
+ typename Q3ValueList<T>::ConstIterator at(typename Q3ValueList<T>::size_type i) const
+ { Q_ASSERT(i < this->size()); return this->begin()+i; }
+ typename Q3ValueList<T>::size_type contains(const T& x) const
+ { return QLinkedList<T>::count(x); }
+
+ Q3ValueList<T>& operator+= (const T& x) { append(x); return *this; }
+
+ T& operator[] (typename Q3ValueList<T>::size_type i) { return *at(i); }
+ const T& operator[] (typename Q3ValueList<T>::size_type i) const { return *at(i); }
+
+};
+
+template <typename T>
+Q_OUTOFLINE_TEMPLATE void Q3ValueList<T>::insert(typename Q3ValueList<T>::Iterator pos,
+ typename Q3ValueList<T>::size_type n, const T& x)
+{
+ for (; n > 0; --n)
+ this->insert(pos, x);
+}
+
+#ifndef QT_NO_DATASTREAM
+template <typename T>
+Q_OUTOFLINE_TEMPLATE QDataStream& operator>>(QDataStream& s, Q3ValueList<T>& l)
+{
+ return operator>>(s, static_cast<QLinkedList<T> &>(l));
+}
+
+template <typename T>
+Q_OUTOFLINE_TEMPLATE QDataStream& operator<<(QDataStream& s, const Q3ValueList<T>& l)
+{
+ return operator<<(s, static_cast<const QLinkedList<T> &>(l));
+}
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3VALUELIST_H
diff --git a/src/qt3support/tools/q3valuelist.qdoc b/src/qt3support/tools/q3valuelist.qdoc
new file mode 100644
index 0000000..a5ebf60
--- /dev/null
+++ b/src/qt3support/tools/q3valuelist.qdoc
@@ -0,0 +1,555 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3ValueList
+ \brief The Q3ValueList class is a value-based template class that
+ provides lists.
+ \compat
+
+ Q3ValueList is a Qt implementation of an STL-like list container.
+ It can be used in your application if the standard \c list is not
+ available for your target platforms.
+
+ Q3ValueList\<T\> defines a template instance to create a list of
+ values that all have the class T. Note that Q3ValueList does not
+ store pointers to the members of the list; it holds a copy of
+ every member. This is why these kinds of classes are called "value
+ based"; Q3PtrList and Q3Dict are "pointer based".
+
+ Q3ValueList contains and manages a collection of objects of type T
+ and provides iterators that allow the contained objects to be
+ addressed. Q3ValueList owns the contained items. For more relaxed
+ ownership semantics, see Q3PtrCollection and friends which are
+ pointer-based containers.
+
+ Some classes cannot be used within a Q3ValueList, for example, all
+ classes derived from QObject and thus all classes that implement
+ widgets. Only values can be used in a Q3ValueList. To qualify as a
+ value the class must provide:
+ \list
+ \i a copy constructor;
+ \i an assignment operator;
+ \i a default constructor, i.e. a constructor that does not take any arguments.
+ \endlist
+
+ Note that C++ defaults to field-by-field assignment operators and
+ copy constructors if no explicit version is supplied. In many
+ cases this is sufficient.
+
+ In addition, some compilers (e.g. Sun CC) might require that the
+ class provides an equality operator (operator==()).
+
+ Q3ValueList's function naming is consistent with the other Qt
+ classes (e.g. count(), isEmpty()). Q3ValueList also provides extra
+ functions for compatibility with STL algorithms, such as size()
+ and empty(). Programmers already familiar with the STL \c list may
+ prefer to use the STL-compatible functions.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3valuelist.cpp 0
+
+
+ Notice that the latest changes to Mary's salary did not affect the
+ value in the list because the list created a copy of Mary's entry.
+
+ There are several ways to find items in the list. The begin() and
+ end() functions return iterators to the beginning and end of the
+ list. The advantage of getting an iterator is that you can move
+ forward or backward from this position by
+ incrementing/decrementing the iterator. The iterator returned by
+ end() points to the item which is one \e past the last item in the
+ container. The past-the-end iterator is still associated with the
+ list it belongs to, however it is \e not dereferenceable;
+ operator*() will not return a well-defined value. If the list is
+ empty(), the iterator returned by begin() will equal the iterator
+ returned by end().
+
+ It is safe to have multiple iterators a the list at the same
+ time. If some member of the list is removed, only iterators
+ pointing to the removed member become invalid. Inserting into the
+ list does not invalidate any iterator. For convenience, the
+ function last() returns a reference to the last item in the list,
+ and first() returns a reference to the first item. If the
+ list is empty(), both last() and first() have undefined behavior
+ (your application will crash or do unpredictable things). Use
+ last() and first() with caution, for example:
+
+ \snippet doc/src/snippets/code/doc_src_q3valuelist.cpp 1
+
+ Because Q3ValueList is value-based there is no need to be careful
+ about deleting items in the list. The list holds its own copies
+ and will free them if the corresponding member or the list itself
+ is deleted. You can force the list to free all of its items with
+ clear().
+
+ Q3ValueList is shared implicitly, which means it can be copied in
+ constant time, i.e. O(1). If multiple Q3ValueList instances share
+ the same data and one needs to modify its contents, this modifying
+ instance makes a copy and modifies its private copy; therefore it
+ does not affect the other instances; this takes O(n) time. This is
+ often called "copy on write". If a Q3ValueList is being used in a
+ multi-threaded program, you must protect all access to the list.
+ See \l QMutex.
+
+ There are several ways to insert items into the list. The
+ prepend() and append() functions insert items at the beginning and
+ the end of the list respectively. The insert() function comes in
+ several flavors and can be used to add one or more items at
+ specific positions within the list.
+
+ Items can also be removed from the list in several ways. There
+ are several variants of the remove() function, which removes a
+ specific item from the list. The remove() function will find and
+ remove items according to a specific item value.
+
+ \sa Q3ValueListIterator
+*/
+
+/*! \typedef Q3ValueList::iterator
+ The list's iterator type, Q3ValueListIterator. */
+/*! \typedef Q3ValueList::const_iterator
+ The list's const iterator type, Q3ValueListConstIterator. */
+/*! \typedef Q3ValueList::value_type
+ The type of the object stored in the list, T. */
+/*! \typedef Q3ValueList::pointer
+ The pointer to T type. */
+/*! \typedef Q3ValueList::const_pointer
+ The const pointer to T type. */
+/*! \typedef Q3ValueList::reference
+ The reference to T type. */
+/*! \typedef Q3ValueList::const_reference
+ The const reference to T type. */
+/*! \typedef Q3ValueList::size_type
+ An unsigned integral type, used to represent various sizes. */
+/*! \typedef Q3ValueList::difference_type
+ \internal
+*/
+/*!
+ \fn Q3ValueList::Q3ValueList()
+
+ Constructs an empty list.
+*/
+
+/*!
+ \fn Q3ValueList::Q3ValueList( const Q3ValueList<T>& l )
+ \fn Q3ValueList::Q3ValueList( const QList<T>& l )
+ \fn Q3ValueList::Q3ValueList( const QLinkedList<T>& l )
+
+ Constructs a copy of \a l.
+*/
+
+/*!
+ \fn Q3ValueList::Q3ValueList( const std::list<T>& l )
+
+ Contructs a copy of \a l.
+
+ This constructor is provided for compatibility with STL
+ containers.
+*/
+
+/*!
+ \fn Q3ValueList::~Q3ValueList()
+
+ Destroys the list. References to the values in the list and all
+ iterators of this list become invalidated. Note that it is
+ impossible for an iterator to check whether or not it is valid:
+ Q3ValueList is highly tuned for performance, not for error
+ checking.
+*/
+
+/*!
+ \fn bool Q3ValueList::operator== ( const Q3ValueList<T>& l ) const
+
+ Compares both lists.
+
+ Returns TRUE if this list and \a l are equal; otherwise returns
+ FALSE.
+*/
+
+/*!
+ \fn bool Q3ValueList::operator== ( const std::list<T>& l ) const
+
+ \overload
+
+ Returns TRUE if this list and \a l are equal; otherwise returns
+ FALSE.
+
+ This operator is provided for compatibility with STL containers.
+*/
+
+/*!
+ \fn Q3ValueList<T>& Q3ValueList::operator= ( const Q3ValueList<T>& l )
+
+ Assigns \a l to this list and returns a reference to this list.
+
+ All iterators of the current list become invalidated by this
+ operation. The cost of such an assignment is O(1) since Q3ValueList
+ is implicitly shared.
+*/
+
+/*!
+ \fn Q3ValueList<T>& Q3ValueList::operator= ( const QList<T>& l )
+
+ Assigns \a l to this list and returns a reference to this list.
+
+ All iterators of the current list become invalidated by this
+ operation.
+*/
+
+/*!
+ \fn Q3ValueList<T>& Q3ValueList::operator= ( const std::list<T>& l )
+
+ \overload
+
+ Assigns the contents of \a l to the list.
+
+ All iterators of the current list become invalidated by this
+ operation.
+*/
+
+/*!
+ \fn bool Q3ValueList::operator!= ( const Q3ValueList<T>& l ) const
+
+ Compares both lists.
+
+ Returns TRUE if this list and \a l are unequal; otherwise returns
+ FALSE.
+*/
+
+/*!
+ \fn iterator Q3ValueList::insert( typename Q3ValueList<T>::Iterator it, const T& x )
+
+ Inserts the value \a x in front of the item pointed to by the
+ iterator, \a it.
+
+ Returns an iterator pointing at the inserted item.
+
+ \sa append(), prepend()
+*/
+
+/*!
+ \fn uint Q3ValueList::remove( const T& x )
+
+ \overload
+
+ Removes all items that have value \a x and returns the number of
+ removed items.
+*/
+
+/*!
+ \fn QDataStream& operator>>( QDataStream& s, Q3ValueList<T>& l )
+
+ \relates Q3ValueList
+
+ Reads a list, \a l, from the stream \a s. The type T stored in the
+ list must implement the streaming operator.
+*/
+
+/*!
+ \fn QDataStream& operator<<( QDataStream& s, const Q3ValueList<T>& l )
+
+ \overload
+ \relates Q3ValueList
+
+ Writes a list, \a l, to the stream \a s. The type T stored in the
+ list must implement the streaming operator.
+*/
+
+/*!
+ \fn void Q3ValueList::insert( typename Q3ValueList<T>::Iterator pos,
+ typename Q3ValueList<T>::size_type n, const T& x )
+
+ \overload
+
+ Inserts \a n copies of \a x before position \a pos.
+*/
+
+/*!
+ \fn Q3ValueList<T>& Q3ValueList::operator<< ( const T& x )
+
+ Adds the value \a x to the end of the list.
+
+ Returns a reference to the list.
+*/
+
+/*!
+ \fn const T& Q3ValueList::operator[] ( typename Q3ValueList<T>::size_type i ) const
+
+ Returns a const reference to the item with index \a i in the list.
+ It is up to you to check whether this item really exists. You can
+ do that easily with the count() function. However this operator
+ does not check whether \a i is in range and will deliver undefined
+ results if it does not exist.
+
+ \warning This function uses a linear search and can be extremely
+ slow for large lists. Q3ValueList is not optimized for random item
+ access. If you need random access use a different container, such
+ as Q3ValueVector.
+*/
+
+/*!
+ \fn T& Q3ValueList::operator[] ( typename Q3ValueList<T>::size_type i )
+
+ \overload
+
+ Returns a non-const reference to the item with index \a i.
+*/
+
+/*!
+ \fn const_iterator Q3ValueList::at( typename Q3ValueList<T>::size_type i ) const
+
+ Returns an iterator pointing to the item at position \a i in the
+ list, or an undefined value if the index is out of range.
+
+ \warning This function uses a linear search and can be extremely
+ slow for large lists. Q3ValueList is not optimized for random item
+ access. If you need random access use a different container, such
+ as Q3ValueVector.
+*/
+
+/*!
+ \fn iterator Q3ValueList::at( typename Q3ValueList<T>::size_type i )
+
+ \overload
+
+ Returns an iterator pointing to the item at position \a i in the
+ list, or an undefined value if the index is out of range.
+
+*/
+
+/*!
+ \fn iterator Q3ValueList::fromLast()
+
+ \overload
+
+ Returns an iterator to the last item in the list, or end() if
+ there is no last item.
+
+ Use the end() function instead. For example:
+
+ \snippet doc/src/snippets/code/doc_src_q3valuelist.cpp 2
+
+*/
+
+/*!
+ \fn const_iterator Q3ValueList::fromLast() const
+
+ Returns an iterator to the last item in the list, or end() if
+ there is no last item.
+
+ Use the end() function instead. For example:
+
+ \snippet doc/src/snippets/code/doc_src_q3valuelist.cpp 3
+
+*/
+
+/*!
+ \fn Q3ValueList<T> Q3ValueList::operator+( const Q3ValueList<T>& l ) const
+
+ Creates a new list and fills it with the items of this list. Then
+ the items of \a l are appended. Returns the new list.
+*/
+
+/*!
+ \fn Q3ValueList<T>& Q3ValueList::operator+= ( const Q3ValueList<T>& l )
+
+ Appends the items of \a l to this list. Returns a reference to
+ this list.
+*/
+
+/*!
+ \fn Q3ValueList<T>& Q3ValueList::operator+= ( const T& x )
+
+ \overload
+
+ Appends the value \a x to the list. Returns a reference to the
+ list.
+*/
+
+/*!
+ \fn iterator Q3ValueList::append( const T& x )
+
+ Inserts \a x at the end of the list.
+
+ \sa insert(), prepend()
+*/
+
+/*!
+ \fn iterator Q3ValueList::prepend( const T& x )
+
+ Inserts \a x at the beginning of the list.
+
+ \sa insert(), append()
+*/
+
+/*!
+ \fn iterator Q3ValueList::remove( typename Q3ValueList<T>::Iterator it )
+
+ Removes the item pointed to by \a it from the list. No iterators
+ other than \a it or other iterators pointing at the same item as
+ \a it are invalidated. Returns an iterator to the next item after
+ \a it, or end() if there is no such item.
+
+ \sa clear()
+*/
+
+/*!
+ \fn uint Q3ValueList::contains( const T& x ) const
+
+ Returns the number of occurrences of the value \a x in the list.
+*/
+
+/*!
+ \class Q3ValueListIterator
+ \brief The Q3ValueListIterator class provides an iterator for Q3ValueList.
+ \compat
+
+ An iterator is a class for accessing the items of a container
+ class: a generalization of the index in an array. A pointer
+ into a "const char *" and an index into an "int[]" are both
+ iterators, and the general idea is to provide that functionality
+ for any data structure.
+
+ The Q3ValueListIterator class is an iterator for Q3ValueList
+ instantiations. You can create the appropriate iterator type by
+ using the \c iterator typedef provided by Q3ValueList.
+
+ The only way to access the items in a Q3ValueList is to use an
+ iterator.
+
+ Example (see Q3ValueList for the complete code):
+ \snippet doc/src/snippets/code/doc_src_q3valuelist.cpp 4
+
+ Q3ValueList is highly optimized for performance and memory usage.
+ This means that you must be careful: Q3ValueList does not know
+ about all its iterators and the iterators don't know to which list
+ they belong. This makes things very fast, but if you're not
+ careful, you can get spectacular bugs. Always make sure iterators
+ are valid before dereferencing them or using them as parameters to
+ generic algorithms in the STL.
+
+ Using an invalid iterator is undefined (your application will
+ probably crash). Many Qt functions return const value lists; to
+ iterate over these you should make a copy and iterate over the
+ copy.
+
+ For every Iterator there is a ConstIterator. When accessing a
+ Q3ValueList in a const environment or if the reference or pointer
+ to the list is itself const, then you must use the ConstIterator.
+ Its semantics are the same as the Iterator, but it only returns
+ const references.
+
+ \sa Q3ValueList, Q3ValueListConstIterator
+*/
+
+/*!
+ \fn Q3ValueListIterator::Q3ValueListIterator()
+
+ Constructs an unitialized iterator.
+*/
+
+/*!
+ \fn Q3ValueListIterator::Q3ValueListIterator(const Q3ValueListIterator &o)
+ \fn Q3ValueListIterator::Q3ValueListIterator(const typename QLinkedList<T>::iterator &o)
+
+ Constucts a copy of iterator \a o.
+*/
+
+/*!
+ \class Q3ValueListConstIterator
+ \brief The Q3ValueListConstIterator class provides a const iterator
+ for Q3ValueList.
+ \compat
+
+ In contrast to Q3ValueListIterator, this class is used to iterate
+ over a const list. It does not allow modification of the values of
+ the list since that would break const semantics.
+
+ You can create the appropriate const iterator type by using the \c
+ const_iterator typedef provided by Q3ValueList.
+
+ For more information on Q3ValueList iterators, see
+ Q3ValueListIterator.
+
+ \sa Q3ValueListIterator, Q3ValueList
+*/
+
+/*!
+ \fn Q3ValueListConstIterator::Q3ValueListConstIterator()
+
+ Constructs an unitialized iterator.
+*/
+
+/*!
+ \fn Q3ValueListConstIterator::Q3ValueListConstIterator(const Q3ValueListConstIterator &o)
+ \fn Q3ValueListConstIterator::Q3ValueListConstIterator(const typename QLinkedList<T>::const_iterator &o)
+ \fn Q3ValueListConstIterator::Q3ValueListConstIterator(const typename QLinkedList<T>::iterator &o)
+
+ Constructs a copy of iterator \a o.
+*/
+
+/*!
+ \typedef Q3ValueList::Iterator
+
+ This iterator is an instantiation of Q3ValueListIterator for the
+ same type as this Q3ValueList. In other words, if you instantiate
+ Q3ValueList<int>, Iterator is a Q3ValueListIterator<int>. Several
+ member function use it, such as Q3ValueList::begin(), which returns
+ an iterator pointing to the first item in the list.
+
+ Functionally, this is almost the same as ConstIterator. The only
+ difference is that you cannot use ConstIterator for non-const
+ operations, and that the compiler can often generate better code
+ if you use ConstIterator.
+
+ \sa Q3ValueListIterator ConstIterator
+*/
+
+/*!
+ \typedef Q3ValueList::ConstIterator
+
+ This iterator is an instantiation of Q3ValueListConstIterator for
+ the same type as this Q3ValueList. In other words, if you
+ instantiate Q3ValueList<int>, ConstIterator is a
+ Q3ValueListConstIterator<int>. Several member function use it, such
+ as Q3ValueList::begin(), which returns an iterator pointing to the
+ first item in the list.
+
+ Functionally, this is almost the same as Iterator. The only
+ difference is you cannot use ConstIterator for non-const
+ operations, and that the compiler can often generate better code
+ if you use ConstIterator.
+
+ \sa Q3ValueListIterator Iterator
+*/
+
+/*!
+ \fn Q3ValueList::operator QList<T>() const
+
+ Automatically converts a Q3ValueList<T> into a QList<T>.
+*/
diff --git a/src/qt3support/tools/q3valuestack.h b/src/qt3support/tools/q3valuestack.h
new file mode 100644
index 0000000..bfa6358
--- /dev/null
+++ b/src/qt3support/tools/q3valuestack.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3VALUESTACK_H
+#define Q3VALUESTACK_H
+
+#include <Qt3Support/q3valuelist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template<class T>
+class Q3ValueStack : public Q3ValueList<T>
+{
+public:
+ Q3ValueStack() {}
+ ~Q3ValueStack() {}
+ void push(const T& val) { this->append(val); }
+ T pop()
+ {
+ T elem(this->last());
+ if (!this->isEmpty())
+ this->remove(this->fromLast());
+ return elem;
+ }
+ T& top() { return this->last(); }
+ const T& top() const { return this->last(); }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3VALUESTACK_H
diff --git a/src/qt3support/tools/q3valuestack.qdoc b/src/qt3support/tools/q3valuestack.qdoc
new file mode 100644
index 0000000..6c2c57b
--- /dev/null
+++ b/src/qt3support/tools/q3valuestack.qdoc
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3ValueStack
+ \brief The Q3ValueStack class is a value-based template class that provides a stack.
+ \compat
+
+ Define a template instance Q3ValueStack\<X\> to create a stack of
+ values that all have the class X.
+
+ Note that Q3ValueStack does not store pointers to the members of
+ the stack; it holds a copy of every member. That is why these
+ kinds of classes are called "value based"; Q3PtrStack, Q3PtrList,
+ Q3Dict, etc., are "pointer based".
+
+ A stack is a last in, first out (LIFO) structure. Items are added
+ to the top of the stack with push() and retrieved from the top
+ with pop(). The top() function provides access to the topmost item
+ without removing it.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3valuestack.cpp 0
+
+ Q3ValueStack is a specialized Q3ValueList provided for convenience.
+ All of Q3ValueList's functionality also applies to Q3PtrStack, for
+ example the facility to iterate over all elements using
+ Q3ValueStack<T>::Iterator. See Q3ValueListIterator for further
+ details.
+
+ Some classes cannot be used within a Q3ValueStack, for example
+ everything derived from QObject and thus all classes that
+ implement widgets. Only values can be used in a Q3ValueStack. To
+ qualify as a value, the class must provide
+ \list
+ \i a copy constructor;
+ \i an assignment operator;
+ \i a default constructor, i.e. a constructor that does not take any arguments.
+ \endlist
+
+ Note that C++ defaults to field-by-field assignment operators and
+ copy constructors if no explicit version is supplied. In many
+ cases this is sufficient.
+*/
+
+
+/*!
+ \fn Q3ValueStack::Q3ValueStack()
+
+ Constructs an empty stack.
+*/
+
+/*!
+ \fn Q3ValueStack::~Q3ValueStack()
+
+ Destroys the stack. References to the values in the stack and all
+ iterators of this stack become invalidated. Because Q3ValueStack is
+ highly tuned for performance, you won't see warnings if you use
+ invalid iterators because it is impossible for an iterator to
+ check whether or not it is valid.
+*/
+
+
+/*!
+ \fn void Q3ValueStack::push( const T& d )
+
+ Adds element, \a d, to the top of the stack. Last in, first out.
+
+ This function is equivalent to append().
+
+ \sa pop(), top()
+*/
+
+/*!
+ \fn T& Q3ValueStack::top()
+
+ Returns a reference to the top item of the stack or the item
+ referenced by end() if no such item exists. Note that you must not
+ change the value the end() iterator points to.
+
+ This function is equivalent to last().
+
+ \sa pop(), push(), Q3ValueList::fromLast()
+*/
+
+
+/*!
+ \fn const T& Q3ValueStack::top() const
+
+ \overload
+
+ Returns a reference to the top item of the stack or the item
+ referenced by end() if no such item exists.
+
+ This function is equivalent to last().
+
+ \sa pop(), push(), Q3ValueList::fromLast()
+*/
+
+/*!
+ \fn T Q3ValueStack::pop()
+
+ Removes the top item from the stack and returns it.
+
+ \sa top() push()
+*/
+
+
+
+
+
diff --git a/src/qt3support/tools/q3valuevector.h b/src/qt3support/tools/q3valuevector.h
new file mode 100644
index 0000000..f2bae4b
--- /dev/null
+++ b/src/qt3support/tools/q3valuevector.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3VALUEVECTOR_H
+#define Q3VALUEVECTOR_H
+
+#include <QtCore/qvector.h>
+
+#ifndef QT_NO_STL
+#include <vector>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+template <typename T>
+class Q3ValueVector : public QVector<T>
+{
+public:
+ inline Q3ValueVector() : QVector<T>() {}
+ inline Q3ValueVector(const Q3ValueVector<T>& v) : QVector<T>(v) {}
+ inline Q3ValueVector(typename QVector<T>::size_type n,
+ const T& val = T()) : QVector<T>(n, val) {}
+
+#ifndef QT_NO_STL
+ inline Q3ValueVector(const std::vector<T>& v) : QVector<T>()
+ { this->resize(v.size()); qCopy(v.begin(), v.end(), this->begin()); }
+#endif
+
+ Q3ValueVector<T>& operator= (const Q3ValueVector<T>& v)
+ { QVector<T>::operator=(v); return *this; }
+
+#ifndef QT_NO_STL
+ Q3ValueVector<T>& operator= (const std::vector<T>& v)
+ {
+ this->clear();
+ this->resize(v.size());
+ qCopy(v.begin(), v.end(), this->begin());
+ return *this;
+ }
+#endif
+
+ void resize(int n, const T& val = T())
+ {
+ if (n < this->size())
+ this->erase(this->begin() + n, this->end());
+ else
+ this->insert(this->end(), n - this->size(), val);
+ }
+
+
+ T& at(int i, bool* ok = 0)
+ {
+ this->detach();
+ if (ok)
+ *ok = (i >= 0 && i < this->size());
+ return *(this->begin() + i);
+ }
+
+ const T&at(int i, bool* ok = 0) const
+ {
+ if (ok)
+ *ok = (i >= 0 && i < this->size());
+ return *(this->begin() + i);
+ }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3VALUEVECTOR_H
diff --git a/src/qt3support/tools/q3valuevector.qdoc b/src/qt3support/tools/q3valuevector.qdoc
new file mode 100644
index 0000000..960bbac
--- /dev/null
+++ b/src/qt3support/tools/q3valuevector.qdoc
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class Q3ValueVector
+ \brief The Q3ValueVector class is a value-based template class that provides a dynamic array.
+ \compat
+
+ Q3ValueVector is a Qt implementation of an STL-like vector
+ container. It can be used in your application if the standard \c
+ vector is not available for your target platforms.
+
+ Q3ValueVector\<T\> defines a template instance to create a vector
+ of values that all have the class T. Q3ValueVector does not store
+ pointers to the members of the vector; it holds a copy of every
+ member. Q3ValueVector is said to be value based; in contrast,
+ Q3PtrList and Q3Dict are pointer based.
+
+ Q3ValueVector contains and manages a collection of objects of type
+ T and provides random access iterators that allow the contained
+ objects to be addressed. Q3ValueVector owns the contained
+ elements. For more relaxed ownership semantics, see Q3PtrCollection
+ and friends, which are pointer-based containers.
+
+ Q3ValueVector provides good performance if you append or remove
+ elements from the end of the vector. If you insert or remove
+ elements from anywhere but the end, performance is very bad. The
+ reason for this is that elements must to be copied into new
+ positions.
+
+ Some classes cannot be used within a Q3ValueVector: for example,
+ all classes derived from QObject and thus all classes that
+ implement widgets. Only values can be used in a Q3ValueVector. To
+ qualify as a value the class must provide:
+ \list
+ \i a copy constructor;
+ \i an assignment operator;
+ \i a default constructor, i.e., a constructor that does not take any arguments.
+ \endlist
+
+ Note that C++ defaults to field-by-field assignment operators and
+ copy constructors if no explicit version is supplied. In many
+ cases this is sufficient.
+
+ Q3ValueVector uses an STL-like syntax to manipulate and address the
+ objects it contains.
+
+ Example:
+ \snippet doc/src/snippets/code/doc_src_q3valuevector.cpp 0
+
+ Program output:
+ \snippet doc/src/snippets/code/doc_src_q3valuevector.cpp 1
+
+ As you can see, the most recent change to Joe's salary did not
+ affect the value in the vector because the vector created a copy
+ of Joe's entry.
+
+ Many Qt functions return const value vectors; to iterate over
+ these you should make a copy and iterate over the copy.
+
+ There are several ways to find items in the vector. The begin()
+ and end() functions return iterators to the beginning and end of
+ the vector. The advantage of getting an iterator is that you can
+ move forward or backward from this position by
+ incrementing/decrementing the iterator. The iterator returned by
+ end() points to the element which is one past the last element in
+ the container. The past-the-end iterator is still associated with
+ the vector it belongs to, however it is \e not dereferenceable;
+ operator*() will not return a well-defined value. If the vector is
+ empty(), the iterator returned by begin() will equal the iterator
+ returned by end().
+
+ The fastest way to access an element of a vector is by using
+ operator[]. This function provides random access and will return
+ a reference to the element located at the specified index. Thus,
+ you can access every element directly, in constant time, providing
+ you know the location of the element. It is undefined to access
+ an element that does not exist (your application will probably
+ crash). For example:
+
+ \snippet doc/src/snippets/code/doc_src_q3valuevector.cpp 2
+
+ Whenever inserting, removing or referencing elements in a vector,
+ always make sure you are referring to valid positions. For
+ example:
+
+ \snippet doc/src/snippets/code/doc_src_q3valuevector.cpp 3
+
+ The iterators provided by vector are random access iterators,
+ therefore you can use them with many generic algorithms, for
+ example, algorithms provided by the STL.
+
+ It is safe to have multiple iterators on the vector at the same
+ time. Since Q3ValueVector manages memory dynamically, all iterators
+ can become invalid if a memory reallocation occurs. For example,
+ if some member of the vector is removed, iterators that point to
+ the removed element and to all following elements become
+ invalidated. Inserting into the middle of the vector will
+ invalidate all iterators. For convenience, the function back()
+ returns a reference to the last element in the vector, and front()
+ returns a reference to the first element. If the vector is
+ empty(), both back() and front() have undefined behavior (your
+ application will crash or do unpredictable things). Use back() and
+ front() with caution, for example:
+
+ \snippet doc/src/snippets/code/doc_src_q3valuevector.cpp 4
+
+ Because Q3ValueVector manages memory dynamically, it is recommended
+ that you contruct a vector with an initial size. Inserting and
+ removing elements happens fastest when:
+ \list
+ \i Inserting or removing elements happens at the end() of the
+ vector;
+ \i The vector does not need to allocate additional memory.
+ \endlist
+
+ By creating a Q3ValueVector with a sufficiently large initial size,
+ there will be less memory allocations. Do not use an initial size
+ that is too big, since it will still take time to construct all
+ the empty entries, and the extra space will be wasted if it is
+ never used.
+
+ Because Q3ValueVector is value-based there is no need to be careful
+ about deleting elements in the vector. The vector holds its own
+ copies and will free them if the corresponding member or the
+ vector itself is deleted. You can force the vector to free all of
+ its items with clear().
+
+ Q3ValueVector is shared implicitly, which means it can be copied in
+ constant time. If multiple Q3ValueVector instances share the same
+ data and one needs to modify its contents, this modifying instance
+ makes a copy and modifies its private copy; it thus does not
+ affect the other instances. This is often called "copy on write".
+ If a Q3ValueVector is being used in a multi-threaded program, you
+ must protect all access to the vector. See QMutex.
+
+ There are several ways to insert elements into the vector. The
+ push_back() function insert elements into the end of the vector,
+ and is usually fastest. The insert() function can be used to add
+ elements at specific positions within the vector.
+
+ Items can be also be removed from the vector in several ways.
+ There are several variants of the erase() function which removes a
+ specific element, or range of elements, from the vector.
+
+ Q3ValueVector stores its elements in contiguous memory. This means
+ that you can use a Q3ValueVector in any situation that requires an
+ array.
+*/
+
+/*!
+ \fn Q3ValueVector::Q3ValueVector()
+
+ Constructs an empty vector without any elements. To create a
+ vector which reserves an initial amount of space for elements, use
+ \c Q3ValueVector(size_type n).
+*/
+
+/*!
+ \fn Q3ValueVector::Q3ValueVector( const Q3ValueVector<T>& v )
+
+ Constructs a copy of \a v.
+
+ This operation costs O(1) time because Q3ValueVector is implicitly
+ shared.
+
+ The first modification to the vector does takes O(n) time, because
+ the elements must be copied.
+*/
+
+/*!
+ \fn Q3ValueVector::Q3ValueVector( const std::vector<T>& v )
+
+ This operation costs O(n) time because \a v is copied.
+*/
+
+/*!
+ \fn Q3ValueVector::Q3ValueVector( QVector<T>::size_type n, const T& val )
+
+ Constructs a vector with an initial size of \a n elements. Each
+ element is initialized with the value of \a val.
+*/
+
+/*!
+ \fn Q3ValueVector<T>& Q3ValueVector::operator=( const Q3ValueVector<T>& v )
+
+ Assigns \a v to this vector and returns a reference to this vector.
+
+ All iterators of the current vector become invalidated by this
+ operation. The cost of such an assignment is O(1) since
+ Q3ValueVector is implicitly shared.
+*/
+
+/*!
+ \fn Q3ValueVector<T>& Q3ValueVector::operator=( const std::vector<T>& v )
+
+ \overload
+
+ Assigns \a v to this vector and returns a reference to this vector.
+
+ All iterators of the current vector become invalidated by this
+ operation. The cost of this assignment is O(n) since \a v is
+ copied.
+*/
+
+/*!
+ \fn T &Q3ValueVector::at( int i , bool* ok )
+
+ Returns a reference to the element with index \a i. If \a ok is
+ non-null, and the index \a i is out of range, *\a ok is set to
+ FALSE and the returned reference is undefined. If the index \a i
+ is within the range of the vector, and \a ok is non-null, *\a ok
+ is set to TRUE and the returned reference is well defined.
+*/
+
+/*!
+ \fn const T &Q3ValueVector::at( int i , bool* ok ) const
+
+ \overload
+
+ Returns a const reference to the element with index \a i. If \a ok
+ is non-null, and the index \a i is out of range, *\a ok is set to
+ FALSE and the returned reference is undefined. If the index \a i
+ is within the range of the vector, and \a ok is non-null, *\a ok
+ is set to TRUE and the returned reference is well defined.
+*/
+
+/*!
+ \fn void Q3ValueVector::resize( int n, const T& val = T() )
+
+ Changes the size of the vector to \a n. If \a n is greater than
+ the current size(), elements are added to the end and initialized
+ with the value of \a val. If \a n is less than size(), elements
+ are removed from the end. If \a n is equal to size() nothing
+ happens.
+*/
diff --git a/src/qt3support/tools/tools.pri b/src/qt3support/tools/tools.pri
new file mode 100644
index 0000000..fa31220
--- /dev/null
+++ b/src/qt3support/tools/tools.pri
@@ -0,0 +1,44 @@
+# Qt compat module
+
+HEADERS += tools/q3asciicache.h \
+ tools/q3asciidict.h \
+ tools/q3signal.h \
+ tools/q3cleanuphandler.h \
+ tools/q3cstring.h \
+ tools/q3deepcopy.h \
+ tools/q3dict.h \
+ tools/q3garray.h \
+ tools/q3gcache.h \
+ tools/q3gdict.h \
+ tools/q3glist.h \
+ tools/q3gvector.h \
+ tools/q3intcache.h \
+ tools/q3intdict.h \
+ tools/q3memarray.h \
+ tools/q3objectdict.h \
+ tools/q3ptrcollection.h \
+ tools/q3ptrdict.h \
+ tools/q3ptrlist.h \
+ tools/q3ptrqueue.h \
+ tools/q3ptrstack.h \
+ tools/q3ptrvector.h \
+ tools/q3semaphore.h \
+ tools/q3shared.h \
+ tools/q3sortedlist.h \
+ tools/q3strlist.h \
+ tools/q3strvec.h \
+ tools/q3tl.h \
+ tools/q3valuelist.h \
+ tools/q3valuestack.h \
+ tools/q3valuevector.h
+
+SOURCES += tools/q3cstring.cpp \
+ tools/q3signal.cpp \
+ tools/q3garray.cpp \
+ tools/q3gcache.cpp \
+ tools/q3gdict.cpp \
+ tools/q3glist.cpp \
+ tools/q3gvector.cpp \
+ tools/q3semaphore.cpp \
+ tools/q3shared.cpp \
+ tools/q3ptrcollection.cpp
diff --git a/src/qt3support/widgets/q3action.cpp b/src/qt3support/widgets/q3action.cpp
new file mode 100644
index 0000000..d79d70a
--- /dev/null
+++ b/src/qt3support/widgets/q3action.cpp
@@ -0,0 +1,2236 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3action.h"
+
+#ifndef QT_NO_ACTION
+
+#include "qevent.h"
+#include "q3toolbar.h"
+#include "qlist.h"
+#include "q3popupmenu.h"
+#include "q3accel.h"
+#include "qtoolbutton.h"
+#include "qcombobox.h"
+#include "qtooltip.h"
+#include "qwhatsthis.h"
+#include "qstatusbar.h"
+#include "qaction.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3Action
+ \brief The Q3Action class provides an abstract user interface
+ action that can appear both in menus and tool bars.
+
+ \compat
+
+ In GUI applications many commands can be invoked via a menu
+ option, a toolbar button and a keyboard accelerator. Since the
+ same action must be performed regardless of how the action was
+ invoked, and since the menu and toolbar should be kept in sync, it
+ is useful to represent a command as an \e action. An action can be
+ added to a menu and a toolbar and will automatically keep them in
+ sync. For example, if the user presses a Bold toolbar button the
+ Bold menu item will automatically be checked.
+
+ A Q3Action may contain an icon, a menu text, an accelerator, a
+ status text, a "What's This?" text and a tool tip. Most of these can
+ be set in the constructor. They can also be set independently with
+ setIconSet(), setText(), setMenuText(), setToolTip(),
+ setStatusTip(), setWhatsThis() and setAccel().
+
+ An action may be a toggle action e.g. a Bold toolbar button, or a
+ command action, e.g. 'Open File' to invoke an open file dialog.
+ Toggle actions emit the toggled() signal when their state changes.
+ Both command and toggle actions emit the activated() signal when
+ they are invoked. Use setToggleAction() to set an action's toggled
+ status. To see if an action is a toggle action use
+ isToggleAction(). A toggle action may be "on", isOn() returns
+ true, or "off", isOn() returns false.
+
+ Actions are added to widgets (menus or toolbars) using addTo(),
+ and removed using removeFrom(). Note that when using Q3ToolBar and
+ Q3PopupMenu, their actions must be Q3Actions.
+
+ Once a Q3Action has been created it should be added to the relevant
+ menu and toolbar and then connected to the slot which will perform
+ the action.
+
+ We recommend that actions are created as children of the window
+ that they are used in. In most cases actions will be children of
+ the application's main window.
+
+ To prevent recursion, don't create an action as a child of a
+ widget that the action is later added to.
+*/
+
+class Q3ActionPrivate
+{
+public:
+ Q3ActionPrivate(Q3Action *act);
+ ~Q3ActionPrivate();
+ QIcon *icon;
+ QString text;
+ QString menutext;
+ QString tooltip;
+ QString statustip;
+ QString whatsthis;
+#ifndef QT_NO_ACCEL
+ QKeySequence key;
+ Q3Accel* accel;
+ int accelid;
+#endif
+ uint enabled : 1;
+ uint visible : 1;
+ uint toggleaction : 1;
+ uint on : 1;
+ uint forceDisabled : 1;
+ uint forceInvisible : 1;
+ Q3ActionGroupPrivate* d_group;
+ Q3Action *action;
+
+ struct MenuItem {
+ MenuItem():popup(0),id(0){}
+ Q3PopupMenu* popup;
+ int id;
+ };
+ // ComboItem is only necessary for actions that are
+ // in dropdown/exclusive actiongroups. The actiongroup
+ // will clean this up
+ struct ComboItem {
+ ComboItem():combo(0), id(0) {}
+ QComboBox *combo;
+ int id;
+ };
+ //just bindings to the Qt4.0 widgets
+ struct Action4Item {
+ Action4Item():widget(0){}
+ QWidget* widget;
+ static QAction *action;
+ };
+ QList<Action4Item *> action4items;
+ QList<MenuItem *> menuitems;
+ QList<QToolButton *> toolbuttons;
+ QList<ComboItem *> comboitems;
+
+ enum Update { Icons = 1, Visibility = 2, State = 4, EverythingElse = 8 };
+ void update(uint upd = EverythingElse);
+
+ QString menuText() const;
+ QString toolTip() const;
+ QString statusTip() const;
+};
+QAction *Q3ActionPrivate::Action4Item::action = 0;
+
+Q3ActionPrivate::Q3ActionPrivate(Q3Action *act)
+ : icon(0),
+#ifndef QT_NO_ACCEL
+ key(0), accel(0), accelid(0),
+#endif
+ enabled(true), visible(true), toggleaction(false), on(false),
+ forceDisabled(false), forceInvisible(false)
+ , d_group(0), action(act)
+{
+}
+
+Q3ActionPrivate::~Q3ActionPrivate()
+{
+ QList<QToolButton*>::Iterator ittb(toolbuttons.begin());
+ QToolButton *tb;
+
+ while (ittb != toolbuttons.end()) {
+ tb = *ittb;
+ ++ittb;
+ delete tb;
+ }
+
+ QList<Q3ActionPrivate::MenuItem*>::Iterator itmi(menuitems.begin());
+ Q3ActionPrivate::MenuItem* mi;
+ while (itmi != menuitems.end()) {
+ mi = *itmi;
+ ++itmi;
+ Q3PopupMenu* menu = mi->popup;
+ if (menu->findItem(mi->id))
+ menu->removeItem(mi->id);
+ }
+ qDeleteAll(menuitems);
+
+ QList<Q3ActionPrivate::Action4Item*>::Iterator itmi4(action4items.begin());
+ Q3ActionPrivate::Action4Item* mi4;
+ while (itmi4 != action4items.end()) {
+ mi4 = *itmi4;
+ ++itmi4;
+ mi4->widget->removeAction(mi4->action);
+ }
+ delete Q3ActionPrivate::Action4Item::action;
+ Q3ActionPrivate::Action4Item::action = 0;
+ qDeleteAll(action4items);
+
+ QList<Q3ActionPrivate::ComboItem*>::Iterator itci(comboitems.begin());
+ Q3ActionPrivate::ComboItem* ci;
+ while (itci != comboitems.end()) {
+ ci = *itci;
+ ++itci;
+ QComboBox* combo = ci->combo;
+ combo->clear();
+ Q3ActionGroup *group = qobject_cast<Q3ActionGroup*>(action->parent());
+ if (group) {
+ QObjectList siblings = group->queryList("Q3Action");
+
+ for (int i = 0; i < siblings.size(); ++i) {
+ Q3Action *sib = qobject_cast<Q3Action*>(siblings.at(i));
+ sib->removeFrom(combo);
+ }
+ for (int i = 0; i < siblings.size(); ++i) {
+ Q3Action *sib = qobject_cast<Q3Action*>(siblings.at(i));
+ if (sib == action)
+ continue;
+ sib->addTo(combo);
+ }
+ }
+ }
+ qDeleteAll(comboitems);
+
+#ifndef QT_NO_ACCEL
+ delete accel;
+#endif
+ delete icon;
+}
+
+class Q3ActionGroupPrivate
+{
+public:
+ uint exclusive: 1;
+ uint dropdown: 1;
+ QList<Q3Action*> actions;
+ Q3Action* selected;
+ Q3Action* separatorAction;
+
+ struct MenuItem {
+ MenuItem():popup(0),id(0){}
+ Q3PopupMenu* popup;
+ int id;
+ };
+ struct Action4Item {
+ Action4Item():widget(0){}
+ QWidget* widget;
+ static QAction *action;
+ };
+ QList<Action4Item *> action4items;
+ QList<QComboBox*> comboboxes;
+ QList<QToolButton*> menubuttons;
+ QList<MenuItem*> menuitems;
+ QList<Q3PopupMenu*> popupmenus;
+
+ void update(const Q3ActionGroup *);
+};
+QAction *Q3ActionGroupPrivate::Action4Item::action = 0;
+
+void Q3ActionPrivate::update(uint upd)
+{
+ for (QList<MenuItem*>::Iterator it(menuitems.begin()); it != menuitems.end(); ++it) {
+ MenuItem* mi = *it;
+ QString t = menuText();
+#ifndef QT_NO_ACCEL
+ if (key)
+ t += QLatin1Char('\t') + (QString)QKeySequence(key);
+#endif
+ if (upd & State) {
+ mi->popup->setItemEnabled(mi->id, enabled);
+ if (toggleaction)
+ mi->popup->setItemChecked(mi->id, on);
+ }
+ if (upd & Visibility)
+ mi->popup->setItemVisible(mi->id, visible);
+
+ if (upd & Icons) {
+ if (icon)
+ mi->popup->changeItem(mi->id, *icon, t);
+ else
+ mi->popup->changeItem(mi->id, QIcon(), t);
+ }
+ if (upd & EverythingElse) {
+ mi->popup->changeItem(mi->id, t);
+ if (!whatsthis.isEmpty())
+ mi->popup->setWhatsThis(mi->id, whatsthis);
+ if (toggleaction) {
+ mi->popup->setCheckable(true);
+ mi->popup->setItemChecked(mi->id, on);
+ }
+ }
+ }
+ if(QAction *act = Action4Item::action) {
+ if (upd & Visibility)
+ act->setVisible(visible);
+ if (upd & Icons) {
+ if (icon)
+ act->setIcon(*icon);
+ else
+ act->setIcon(QIcon());
+ }
+ if (upd & EverythingElse) {
+ QString text = action->menuText();
+#ifndef QT_NO_ACCEL
+ if (key)
+ text += QLatin1Char('\t') + (QString)QKeySequence(key);
+#endif
+ act->setText(text);
+ act->setToolTip(statusTip());
+ act->setWhatsThis(whatsthis);
+ }
+ }
+ for (QList<QToolButton*>::Iterator it2(toolbuttons.begin()); it2 != toolbuttons.end(); ++it2) {
+ QToolButton* btn = *it2;
+ if (upd & State) {
+ btn->setEnabled(enabled);
+ if (toggleaction)
+ btn->setOn(on);
+ }
+ if (upd & Visibility)
+ visible ? btn->show() : btn->hide();
+ if (upd & Icons) {
+ if (icon)
+ btn->setIconSet(*icon);
+ else
+ btn->setIconSet(QIcon());
+ }
+ if (upd & EverythingElse) {
+ btn->setToggleButton(toggleaction);
+ if (!text.isEmpty())
+ btn->setTextLabel(text, false);
+#ifndef QT_NO_TOOLTIP
+ btn->setToolTip(toolTip());
+#endif
+#ifndef QT_NO_STATUSTIP
+ btn->setStatusTip(statusTip());
+#endif
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::remove(btn);
+ if (!whatsthis.isEmpty())
+ QWhatsThis::add(btn, whatsthis);
+#endif
+ }
+ }
+#ifndef QT_NO_ACCEL
+ if (accel) {
+ accel->setEnabled(enabled && visible);
+ if (!whatsthis.isEmpty())
+ accel->setWhatsThis(accelid, whatsthis);
+ }
+#endif
+ // Only used by actiongroup
+ for (QList<ComboItem*>::Iterator it3(comboitems.begin()); it3 != comboitems.end(); ++it3) {
+ ComboItem *ci = *it3;
+ if (!ci->combo)
+ return;
+ if (ci->id == -1) {
+ ci->id = ci->combo->count();
+ if (icon)
+ ci->combo->insertItem(icon->pixmap(), text);
+ else
+ ci->combo->insertItem(text);
+ } else {
+ if (icon)
+ ci->combo->changeItem(icon->pixmap(), text, ci->id);
+ else
+ ci->combo->changeItem(text, ci->id);
+ }
+ }
+}
+
+QString Q3ActionPrivate::menuText() const
+{
+ if (menutext.isNull()) {
+ QString t(text);
+ t.replace(QLatin1Char('&'), QLatin1String("&&"));
+ return t;
+ }
+ return menutext;
+}
+
+QString Q3ActionPrivate::toolTip() const
+{
+ if (tooltip.isNull()) {
+#ifndef QT_NO_ACCEL
+ if (accel)
+ return text + QLatin1String(" (") + (QString)QKeySequence(accel->key(accelid)) + QLatin1Char(')');
+#endif
+ return text;
+ }
+ return tooltip;
+}
+
+QString Q3ActionPrivate::statusTip() const
+{
+ if (statustip.isNull())
+ return toolTip();
+ return statustip;
+}
+
+/*
+ internal: guesses a descriptive text from a menu text
+ */
+static QString qt_stripMenuText(QString s)
+{
+ s.remove(QLatin1String("..."));
+ s.remove(QLatin1Char('&'));
+ return s.trimmed();
+}
+
+/*!
+ Constructs an action called \a name with parent \a parent.
+
+ If \a parent is a Q3ActionGroup, the new action inserts itself into
+ \a parent.
+
+ For accelerators and status tips to work, \a parent must either be
+ a widget, or an action group whose parent is a widget.
+
+ \warning To prevent recursion, don't create an action as a child
+ of a widget that the action is later added to.
+*/
+Q3Action::Q3Action(QObject* parent, const char* name)
+ : QObject(parent, name)
+{
+ d = new Q3ActionPrivate(this);
+ init();
+}
+
+/*!
+ Constructs an action called \a name with parent \a parent.
+
+ If \a toggle is true the action will be a toggle action, otherwise
+ it will be a command action.
+
+ If \a parent is a Q3ActionGroup, the new action inserts itself into
+ \a parent.
+
+ For accelerators and status tips to work, \a parent must either be
+ a widget, or an action group whose parent is a widget.
+*/
+Q3Action::Q3Action(QObject* parent, const char* name, bool toggle)
+ : QObject(parent, name)
+{
+ d = new Q3ActionPrivate(this);
+ d->toggleaction = toggle;
+ init();
+}
+
+
+#ifndef QT_NO_ACCEL
+
+/*!
+ This constructor creates an action with the following properties:
+ the icon or icon \a icon, the menu text \a menuText and
+ keyboard accelerator \a accel. It is a child of \a parent and
+ called \a name.
+
+ If \a parent is a Q3ActionGroup, the action automatically becomes
+ a member of it.
+
+ For accelerators and status tips to work, \a parent must either be
+ a widget, or an action group whose parent is a widget.
+
+ The action uses a stripped version of \a menuText (e.g. "\&Menu
+ Option..." becomes "Menu Option") as descriptive text for
+ tool buttons. You can override this by setting a specific
+ description with setText(). The same text and \a accel will be
+ used for tool tips and status tips unless you provide text for
+ these using setToolTip() and setStatusTip().
+
+ Call setToggleAction(true) to make the action a toggle action.
+
+ \warning To prevent recursion, don't create an action as a child
+ of a widget that the action is later added to.
+*/
+Q3Action::Q3Action(const QIcon& icon, const QString& menuText, QKeySequence accel,
+ QObject* parent, const char* name)
+ : QObject(parent, name)
+{
+ d = new Q3ActionPrivate(this);
+ if (!icon.isNull())
+ setIconSet(icon);
+ d->text = qt_stripMenuText(menuText);
+ d->menutext = menuText;
+ setAccel(accel);
+ init();
+}
+
+/*!
+ This constructor results in an icon-less action with the menu
+ text \a menuText and keyboard accelerator \a accel. It is a child
+ of \a parent and called \a name.
+
+ If \a parent is a Q3ActionGroup, the action automatically becomes
+ a member of it.
+
+ For accelerators and status tips to work, \a parent must either be
+ a widget, or an action group whose parent is a widget.
+
+ The action uses a stripped version of \a menuText (e.g. "\&Menu
+ Option..." becomes "Menu Option") as descriptive text for
+ tool buttons. You can override this by setting a specific
+ description with setText(). The same text and \a accel will be
+ used for tool tips and status tips unless you provide text for
+ these using setToolTip() and setStatusTip().
+
+ Call setToggleAction(true) to make the action a toggle action.
+
+ \warning To prevent recursion, don't create an action as a child
+ of a widget that the action is later added to.
+*/
+Q3Action::Q3Action(const QString& menuText, QKeySequence accel,
+ QObject* parent, const char* name)
+ : QObject(parent, name)
+{
+ d = new Q3ActionPrivate(this);
+ d->text = qt_stripMenuText(menuText);
+ d->menutext = menuText;
+ setAccel(accel);
+ init();
+}
+
+/*!
+ This constructor creates an action with the following properties:
+ the description \a text, the icon or icon \a icon, the menu
+ text \a menuText and keyboard accelerator \a accel. It is a child
+ of \a parent and called \a name. If \a toggle is true the action
+ will be a toggle action, otherwise it will be a command action.
+
+ If \a parent is a Q3ActionGroup, the action automatically becomes
+ a member of it.
+
+ For accelerators and status tips to work, \a parent must either be
+ a widget, or an action group whose parent is a widget.
+
+ The \a text and \a accel will be used for tool tips and status
+ tips unless you provide specific text for these using setToolTip()
+ and setStatusTip().
+*/
+Q3Action::Q3Action(const QString& text, const QIcon& icon, const QString& menuText, QKeySequence accel, QObject* parent, const char* name, bool toggle)
+ : QObject(parent, name)
+{
+ d = new Q3ActionPrivate(this);
+ d->toggleaction = toggle;
+ if (!icon.isNull())
+ setIconSet(icon);
+
+ d->text = text;
+ d->menutext = menuText;
+ setAccel(accel);
+ init();
+}
+
+/*!
+ This constructor results in an icon-less action with the
+ description \a text, the menu text \a menuText and the keyboard
+ accelerator \a accel. Its parent is \a parent and it is called \a
+ name. If \a toggle is true the action will be a toggle action,
+ otherwise it will be a command action.
+
+ The action automatically becomes a member of \a parent if \a
+ parent is a Q3ActionGroup.
+
+ For accelerators and status tips to work, \a parent must either be
+ a widget, or an action group whose parent is a widget.
+
+ The \a text and \a accel will be used for tool tips and status
+ tips unless you provide specific text for these using setToolTip()
+ and setStatusTip().
+*/
+Q3Action::Q3Action(const QString& text, const QString& menuText, QKeySequence accel, QObject* parent, const char* name, bool toggle)
+ : QObject(parent, name)
+{
+ d = new Q3ActionPrivate(this);
+ d->toggleaction = toggle;
+ d->text = text;
+ d->menutext = menuText;
+ setAccel(accel);
+ init();
+}
+#endif
+
+/*!
+ \internal
+*/
+void Q3Action::init()
+{
+ if (qobject_cast<Q3ActionGroup*>(parent()))
+ ((Q3ActionGroup*) parent())->add(this); // insert into action group
+}
+
+/*!
+ Destroys the object and frees allocated resources.
+*/
+
+Q3Action::~Q3Action()
+{
+ delete d;
+}
+
+/*!
+ \property Q3Action::iconSet
+ \brief the action's icon
+
+ The icon is used as the tool button icon and in the menu to the
+ left of the menu text. There is no default icon.
+
+ If a null icon (QIcon::isNull() is passed into this function,
+ the icon of the action is cleared.
+
+ (See the action/toggleaction/toggleaction.cpp example.)
+
+*/
+void Q3Action::setIconSet(const QIcon& icon)
+{
+ register QIcon *i = d->icon;
+ if (!icon.isNull())
+ d->icon = new QIcon(icon);
+ else
+ d->icon = 0;
+ delete i;
+ d->update(Q3ActionPrivate::Icons);
+}
+
+QIcon Q3Action::iconSet() const
+{
+ if (d->icon)
+ return *d->icon;
+ return QIcon();
+}
+
+/*!
+ \property Q3Action::text
+ \brief the action's descriptive text
+
+ \sa setMenuText() setToolTip() setStatusTip()
+*/
+void Q3Action::setText(const QString& text)
+{
+ d->text = text;
+ d->update();
+}
+
+QString Q3Action::text() const
+{
+ return d->text;
+}
+
+
+/*!
+ \property Q3Action::menuText
+ \brief the action's menu text
+
+ If the action is added to a menu the menu option will consist of
+ the icon (if there is one), the menu text and the accelerator (if
+ there is one). If the menu text is not explicitly set in the
+ constructor or by using setMenuText() the action's description
+ text will be used as the menu text. There is no default menu text.
+
+ \sa text
+*/
+void Q3Action::setMenuText(const QString& text)
+{
+ if (d->menutext == text)
+ return;
+
+ d->menutext = text;
+ d->update();
+}
+
+QString Q3Action::menuText() const
+{
+ return d->menuText();
+}
+
+/*!
+ \property Q3Action::toolTip
+ \brief the action's tool tip
+
+ This text is used for the tool tip. If no status tip has been set
+ the tool tip will be used for the status tip.
+
+ If no tool tip is specified the action's text is used, and if that
+ hasn't been specified the description text is used as the tool tip
+ text.
+
+ There is no default tool tip text.
+
+ \sa setStatusTip() setAccel()
+*/
+void Q3Action::setToolTip(const QString& tip)
+{
+ if (d->tooltip == tip)
+ return;
+
+ d->tooltip = tip;
+ d->update();
+}
+
+QString Q3Action::toolTip() const
+{
+ return d->toolTip();
+}
+
+/*!
+ \property Q3Action::statusTip
+ \brief the action's status tip
+
+ The statusTip is displayed on all status bars that this action's
+ top-level parent widget provides.
+
+ If no status tip is defined, the action uses the tool tip text.
+
+ There is no default statusTip text.
+
+ \sa setToolTip()
+*/
+void Q3Action::setStatusTip(const QString& tip)
+{
+ // Old comment: ### Please reimp for Q3ActionGroup!
+ // For consistency reasons even action groups should show
+ // status tips (as they already do with tool tips)
+ // Please change Q3ActionGroup class doc appropriately after
+ // reimplementation.
+
+ if (d->statustip == tip)
+ return;
+
+ d->statustip = tip;
+ d->update();
+}
+
+QString Q3Action::statusTip() const
+{
+ return d->statusTip();
+}
+
+/*!
+ \property Q3Action::whatsThis
+ \brief the action's "What's This?" help text
+
+ The whats this text is used to provide a brief description of the
+ action. The text may contain rich text (HTML-like tags -- see
+ QStyleSheet for the list of supported tags). There is no default
+ "What's This?" text.
+
+ \sa QWhatsThis
+*/
+void Q3Action::setWhatsThis(const QString& whatsThis)
+{
+ if (d->whatsthis == whatsThis)
+ return;
+ d->whatsthis = whatsThis;
+ d->update();
+}
+
+QString Q3Action::whatsThis() const
+{
+ return d->whatsthis;
+}
+
+
+#ifndef QT_NO_ACCEL
+/*!
+ \property Q3Action::accel
+ \brief the action's accelerator key
+
+ The keycodes can be found in \l Qt::Key and \l Qt::Modifier. There
+ is no default accelerator key.
+*/
+//#### Please reimp for Q3ActionGroup!
+//#### For consistency reasons even Q3ActionGroups should respond to
+//#### their accelerators and e.g. open the relevant submenu.
+//#### Please change appropriate Q3ActionGroup class doc after
+//#### reimplementation.
+void Q3Action::setAccel(const QKeySequence& key)
+{
+ if (d->key == key)
+ return;
+
+ d->key = key;
+ delete d->accel;
+ d->accel = 0;
+
+ if (!(int)key) {
+ d->update();
+ return;
+ }
+
+ QObject* p = parent();
+ while (p && !p->isWidgetType()) {
+ p = p->parent();
+ }
+ if (p) {
+ d->accel = new Q3Accel((QWidget*)p, this, "qt_action_accel");
+ d->accelid = d->accel->insertItem(d->key);
+ d->accel->connectItem(d->accelid, this, SLOT(internalActivation()));
+ } else
+ qWarning("Q3Action::setAccel() (%s) requires widget in parent chain", objectName().toLocal8Bit().data());
+ d->update();
+}
+
+
+QKeySequence Q3Action::accel() const
+{
+ return d->key;
+}
+#endif
+
+
+/*!
+ \property Q3Action::toggleAction
+ \brief whether the action is a toggle action
+
+ A toggle action is one which has an on/off state. For example a
+ Bold toolbar button is either on or off. An action which is not a
+ toggle action is a command action; a command action is simply
+ executed, e.g. file save. This property's default is false.
+
+ In some situations, the state of one toggle action should depend
+ on the state of others. For example, "Left Align", "Center" and
+ "Right Align" toggle actions are mutually exclusive. To achieve
+ exclusive toggling, add the relevant toggle actions to a
+ Q3ActionGroup with the \l Q3ActionGroup::exclusive property set to
+ true.
+*/
+void Q3Action::setToggleAction(bool enable)
+{
+ if (enable == (bool)d->toggleaction)
+ return;
+
+ if (!enable)
+ d->on = false;
+
+ d->toggleaction = enable;
+ d->update();
+}
+
+bool Q3Action::isToggleAction() const
+{
+ return d->toggleaction;
+}
+
+/*!
+ Activates the action and executes all connected slots.
+ This only works for actions that are not toggle actions.
+
+ \sa toggle()
+*/
+void Q3Action::activate()
+{
+ if (isToggleAction()) {
+#if defined(QT_CHECK_STATE)
+ qWarning("Q3Action::%s() (%s) Toggle actions "
+ "can not be activated", "activate", objectName().toLocal8Bit().data());
+#endif
+ return;
+ }
+ emit activated();
+}
+
+/*!
+ Toggles the state of a toggle action.
+
+ \sa on, activate(), toggled(), isToggleAction()
+*/
+void Q3Action::toggle()
+{
+ if (!isToggleAction()) {
+ qWarning("Q3Action::%s() (%s) Only toggle actions "
+ "can be switched", "toggle", objectName().toLocal8Bit().data());
+ return;
+ }
+ setOn(!isOn());
+}
+
+/*!
+ \property Q3Action::on
+ \brief whether a toggle action is on
+
+ This property is always on (true) for command actions and
+ \l{Q3ActionGroup}s; setOn() has no effect on them. For action's
+ where isToggleAction() is true, this property's default value is
+ off (false).
+
+ \sa toggleAction
+*/
+void Q3Action::setOn(bool enable)
+{
+ if (!isToggleAction()) {
+ if (enable)
+ qWarning("Q3Action::%s() (%s) Only toggle actions "
+ "can be switched", "setOn", objectName().toLocal8Bit().data());
+ return;
+ }
+ if (enable == (bool)d->on)
+ return;
+ d->on = enable;
+ d->update(Q3ActionPrivate::State);
+ emit toggled(enable);
+}
+
+bool Q3Action::isOn() const
+{
+ return d->on;
+}
+
+/*!
+ \property Q3Action::enabled
+ \brief whether the action is enabled
+
+ Disabled actions can't be chosen by the user. They don't disappear
+ from the menu/tool bar but are displayed in a way which indicates
+ that they are unavailable, e.g. they might be displayed grayed
+ out.
+
+ What's this? help on disabled actions is still available provided
+ the \l Q3Action::whatsThis property is set.
+*/
+void Q3Action::setEnabled(bool enable)
+{
+ d->forceDisabled = !enable;
+
+ if ((bool)d->enabled == enable)
+ return;
+
+ d->enabled = enable;
+ d->update(Q3ActionPrivate::State);
+}
+
+bool Q3Action::isEnabled() const
+{
+ return d->enabled;
+}
+
+/*!
+ Disables the action if \a disable is true; otherwise
+ enables the action.
+
+ See the \l enabled documentation for more information.
+*/
+void Q3Action::setDisabled(bool disable)
+{
+ setEnabled(!disable);
+}
+
+/*!
+ \property Q3Action::visible
+ \brief whether the action can be seen (e.g. in menus and toolbars)
+
+ If \e visible is true the action can be seen (e.g. in menus and
+ toolbars) and chosen by the user; if \e visible is false the
+ action cannot be seen or chosen by the user.
+
+ Actions which are not visible are \e not grayed out; they do not
+ appear at all.
+*/
+void Q3Action::setVisible(bool visible)
+{
+ d->forceInvisible = !visible;
+
+ if ((bool)d->visible == visible)
+ return;
+
+ d->visible = visible;
+ d->update(Q3ActionPrivate::Visibility);
+}
+
+/*
+ Returns true if the action is visible (e.g. in menus and
+ toolbars); otherwise returns false.
+*/
+bool Q3Action::isVisible() const
+{
+ return d->visible;
+}
+
+/*! \internal
+*/
+void Q3Action::internalActivation()
+{
+ if (isToggleAction())
+ setOn(!isOn());
+ emit activated();
+}
+
+/*! \internal
+*/
+void Q3Action::toolButtonToggled(bool on)
+{
+ if (!isToggleAction())
+ return;
+ setOn(on);
+}
+
+/*!
+ Adds this action to widget \a w.
+
+ Currently actions may be added to Q3ToolBar and Q3PopupMenu widgets.
+
+ An action added to a tool bar is automatically displayed as a tool
+ button; an action added to a pop up menu appears as a menu option.
+
+ addTo() returns true if the action was added successfully and
+ false otherwise. (If \a w is not a Q3ToolBar or Q3PopupMenu the
+ action will not be added and false will be returned.)
+
+ \sa removeFrom()
+*/
+bool Q3Action::addTo(QWidget* w)
+{
+#ifndef QT_NO_TOOLBAR
+ if (qobject_cast<Q3ToolBar*>(w)) {
+ if (objectName() == QLatin1String("qt_separator_action")) {
+ ((Q3ToolBar*)w)->addSeparator();
+ } else {
+ QString bname = objectName() + QLatin1String("_action_button");
+ QToolButton* btn = new QToolButton((Q3ToolBar*) w);
+ btn->setObjectName(bname);
+ addedTo(btn, w);
+ btn->setToggleButton(d->toggleaction);
+ d->toolbuttons.append(btn);
+ if (d->icon)
+ btn->setIconSet(*d->icon);
+ d->update(Q3ActionPrivate::State | Q3ActionPrivate::Visibility | Q3ActionPrivate::EverythingElse) ;
+ connect(btn, SIGNAL(clicked()), this, SIGNAL(activated()));
+ connect(btn, SIGNAL(toggled(bool)), this, SLOT(toolButtonToggled(bool)));
+ connect(btn, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ }
+ } else
+#endif
+ if (qobject_cast<Q3PopupMenu*>(w)) {
+ Q3ActionPrivate::MenuItem* mi = new Q3ActionPrivate::MenuItem;
+ mi->popup = (Q3PopupMenu*) w;
+ QIcon* dicon = d->icon;
+ if (objectName() == QLatin1String("qt_separator_action"))
+ mi->id = ((Q3PopupMenu*)w)->insertSeparator();
+ else if (dicon)
+ mi->id = mi->popup->insertItem(*dicon, QString::fromLatin1(""));
+ else
+ mi->id = mi->popup->insertItem(QString::fromLatin1(""));
+ addedTo(mi->popup->indexOf(mi->id), mi->popup);
+ mi->popup->connectItem(mi->id, this, SLOT(internalActivation()));
+ d->menuitems.append(mi);
+ d->update(Q3ActionPrivate::State | Q3ActionPrivate::Visibility | Q3ActionPrivate::EverythingElse);
+ connect(mi->popup, SIGNAL(highlighted(int)), this, SLOT(menuStatusText(int)));
+ connect(mi->popup, SIGNAL(aboutToHide()), this, SLOT(clearStatusText()));
+ connect(mi->popup, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ // Makes only sense when called by Q3ActionGroup::addTo
+ } else if (qobject_cast<QComboBox*>(w)) {
+ Q3ActionPrivate::ComboItem *ci = new Q3ActionPrivate::ComboItem;
+ ci->combo = (QComboBox*)w;
+ connect(ci->combo, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ ci->id = ci->combo->count();
+ if (objectName() == QLatin1String("qt_separator_action")) {
+ if (d->icon)
+ ci->combo->insertItem(d->icon->pixmap(), text());
+ else
+ ci->combo->insertItem(text());
+ } else {
+ ci->id = -1;
+ }
+ d->comboitems.append(ci);
+ d->update(Q3ActionPrivate::State | Q3ActionPrivate::EverythingElse);
+ } else if(qobject_cast<QMenu*>(w)) {
+ Q3ActionPrivate::Action4Item *act = new Q3ActionPrivate::Action4Item;
+ if(!act->action) { //static
+ act->action = new QAction(this);
+ if (objectName() == QLatin1String("qt_separator_action"))
+ act->action->setSeparator(true);
+ }
+ act->widget = w;
+ act->widget->addAction(act->action);
+ d->action4items.append(act);
+ d->update(Q3ActionPrivate::State | Q3ActionPrivate::EverythingElse);
+ } else {
+ qWarning("Q3Action::addTo(), unknown object");
+ return false;
+ }
+ return true;
+}
+
+/*!
+ This function is called from the addTo() function when it has
+ created a widget (\a actionWidget) for the action in the \a
+ container.
+*/
+
+void Q3Action::addedTo(QWidget *actionWidget, QWidget *container)
+{
+ Q_UNUSED(actionWidget);
+ Q_UNUSED(container);
+}
+
+/*!
+ \overload
+
+ This function is called from the addTo() function when it has
+ created a menu item at the index position \a index in the popup
+ menu \a menu.
+*/
+
+void Q3Action::addedTo(int index, Q3PopupMenu *menu)
+{
+ Q_UNUSED(index);
+ Q_UNUSED(menu);
+}
+
+/*!
+ Sets the status message to \a text
+*/
+void Q3Action::showStatusText(const QString& text)
+{
+#ifndef QT_NO_STATUSBAR
+ // find out whether we are clearing the status bar by the popup that actually set the text
+ static Q3PopupMenu *lastmenu = 0;
+ QObject *s = (QObject*)sender();
+ if (s) {
+ Q3PopupMenu *menu = qobject_cast<Q3PopupMenu*>(s);
+ if (menu && text.size())
+ lastmenu = menu;
+ else if (menu && text.isEmpty()) {
+ if (lastmenu && menu != lastmenu)
+ return;
+ lastmenu = 0;
+ }
+ }
+
+ QObject* par = parent();
+ QObject* lpar = 0;
+ QStatusBar *bar = 0;
+ while (par && !bar) {
+ lpar = par;
+ bar = (QStatusBar*)par->child(0, "QStatusBar", false);
+ par = par->parent();
+ }
+ if (!bar && lpar) {
+ QObjectList l = lpar->queryList("QStatusBar");
+ if (l.isEmpty())
+ return;
+ // #### hopefully the last one is the one of the mainwindow...
+ bar = static_cast<QStatusBar*>(l.at(l.size()-1));
+ }
+ if (bar) {
+ if (text.isEmpty())
+ bar->clearMessage();
+ else
+ bar->showMessage(text);
+ }
+#endif
+}
+
+/*!
+ Sets the status message to the menu item's status text, or to the
+ tooltip, if there is no status text.
+*/
+void Q3Action::menuStatusText(int id)
+{
+ static int lastId = 0;
+ QString text;
+ QList<Q3ActionPrivate::MenuItem*>::Iterator it(d->menuitems.begin());
+ while (it != d->menuitems.end()) {
+ if ((*it)->id == id) {
+ text = statusTip();
+ break;
+ }
+ ++it;
+ }
+
+ if (!text.isEmpty())
+ showStatusText(text);
+ else if (id != lastId)
+ clearStatusText();
+ lastId = id;
+}
+
+/*!
+ Clears the status text.
+*/
+void Q3Action::clearStatusText()
+{
+ if (!statusTip().isEmpty())
+ showStatusText(QString());
+}
+
+/*!
+ Removes the action from widget \a w.
+
+ Returns true if the action was removed successfully; otherwise
+ returns false.
+
+ \sa addTo()
+*/
+bool Q3Action::removeFrom(QWidget* w)
+{
+#ifndef QT_NO_TOOLBAR
+ if (qobject_cast<Q3ToolBar*>(w)) {
+ QList<QToolButton*>::Iterator it(d->toolbuttons.begin());
+ QToolButton* btn;
+ while (it != d->toolbuttons.end()) {
+ btn = *it;
+ ++it;
+ if (btn->parentWidget() == w) {
+ d->toolbuttons.removeAll(btn);
+ disconnect(btn, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ delete btn;
+ // no need to disconnect from status bar
+ }
+ }
+ } else
+#endif
+ if (qobject_cast<Q3PopupMenu*>(w)) {
+ QList<Q3ActionPrivate::MenuItem*>::Iterator it(d->menuitems.begin());
+ Q3ActionPrivate::MenuItem* mi;
+ while (it != d->menuitems.end()) {
+ mi = *it;
+ ++it;
+ if (mi->popup == w) {
+ disconnect(mi->popup, SIGNAL(highlighted(int)), this, SLOT(menuStatusText(int)));
+ disconnect(mi->popup, SIGNAL(aboutToHide()), this, SLOT(clearStatusText()));
+ disconnect(mi->popup, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ mi->popup->removeItem(mi->id);
+ d->menuitems.removeAll(mi);
+ delete mi;
+ }
+ }
+ } else if (qobject_cast<QComboBox*>(w)) {
+ QList<Q3ActionPrivate::ComboItem*>::Iterator it(d->comboitems.begin());
+ Q3ActionPrivate::ComboItem *ci;
+ while (it != d->comboitems.end()) {
+ ci = *it;
+ ++it;
+ if (ci->combo == w) {
+ disconnect(ci->combo, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ d->comboitems.removeAll(ci);
+ delete ci;
+ }
+ }
+ } else if (qobject_cast<QMenu*>(w)) {
+ QList<Q3ActionPrivate::Action4Item*>::Iterator it(d->action4items.begin());
+ Q3ActionPrivate::Action4Item *a4i;
+ while (it != d->action4items.end()) {
+ a4i = *it;
+ ++it;
+ if (a4i->widget == w) {
+ a4i->widget->removeAction(a4i->action);
+ d->action4items.removeAll(a4i);
+ delete a4i;
+ }
+ }
+ } else {
+ qWarning("Q3Action::removeFrom(), unknown object");
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+*/
+void Q3Action::objectDestroyed()
+{
+ const QObject* obj = sender();
+ Q3ActionPrivate::MenuItem* mi;
+ for (int i = 0; i < d->menuitems.size();) {
+ mi = d->menuitems.at(i);
+ ++i;
+ if (mi->popup == obj) {
+ d->menuitems.removeAll(mi);
+ delete mi;
+ }
+ }
+ Q3ActionPrivate::ComboItem *ci;
+ QList<Q3ActionPrivate::ComboItem*>::Iterator it2(d->comboitems.begin());
+ while (it2 != d->comboitems.end()) {
+ ci = *it2;
+ ++it2;
+ if (ci->combo == obj) {
+ d->comboitems.removeAll(ci);
+ delete ci;
+ }
+ }
+ d->toolbuttons.removeAll((QToolButton *)obj);
+}
+
+/*!
+ \fn void Q3Action::activated()
+
+ This signal is emitted when an action is activated by the user,
+ e.g. when the user clicks a menu option or a toolbar button or
+ presses an action's accelerator key combination.
+
+ Connect to this signal for command actions. Connect to the
+ toggled() signal for toggle actions.
+*/
+
+/*!
+ \fn void Q3Action::toggled(bool on)
+
+ This signal is emitted when a toggle action changes state; command
+ actions and \l{Q3ActionGroup}s don't emit toggled().
+
+ The \a on argument denotes the new state: If \a on is true the
+ toggle action is switched on, and if \a on is false the toggle
+ action is switched off.
+
+ To trigger a user command depending on whether a toggle action has
+ been switched on or off connect it to a slot that takes a bool to
+ indicate the state.
+
+ \sa activated() setToggleAction() setOn()
+*/
+
+void Q3ActionGroupPrivate::update(const Q3ActionGroup* that)
+{
+ for (QList<Q3Action*>::Iterator it(actions.begin()); it != actions.end(); ++it) {
+ if (that->isEnabled() && !(*it)->d->forceDisabled)
+ (*it)->setEnabled(true);
+ else if (!that->isEnabled() && (*it)->isEnabled()) {
+ (*it)->setEnabled(false);
+ (*it)->d->forceDisabled = false;
+ }
+ if (that->isVisible() && !(*it)->d->forceInvisible) {
+ (*it)->setVisible(true);
+ } else if (!that->isVisible() && (*it)->isVisible()) {
+ (*it)->setVisible(false);
+ (*it)->d->forceInvisible = false;
+ }
+ }
+ for (QList<QComboBox*>::Iterator cb(comboboxes.begin()); cb != comboboxes.end(); ++cb) {
+ QComboBox *combobox = *cb;
+ combobox->setEnabled(that->isEnabled());
+ combobox->setShown(that->isVisible());
+
+#ifndef QT_NO_TOOLTIP
+ QToolTip::remove(combobox);
+ if (that->toolTip().size())
+ QToolTip::add(combobox, that->toolTip());
+#endif
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::remove(combobox);
+ if (that->whatsThis().size())
+ QWhatsThis::add(combobox, that->whatsThis());
+#endif
+
+ }
+ for (QList<QToolButton*>::Iterator mb(menubuttons.begin()); mb != menubuttons.end(); ++mb) {
+ QToolButton *button = *mb;
+ button->setEnabled(that->isEnabled());
+ button->setShown(that->isVisible());
+
+ if (!that->text().isNull())
+ button->setTextLabel(that->text());
+ if (!that->iconSet().isNull())
+ button->setIconSet(that->iconSet());
+
+#ifndef QT_NO_TOOLTIP
+ QToolTip::remove(*mb);
+ if (that->toolTip().size())
+ QToolTip::add(button, that->toolTip());
+#endif
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::remove(button);
+ if (that->whatsThis().size())
+ QWhatsThis::add(button, that->whatsThis());
+#endif
+ }
+ if(QAction *act = Q3ActionGroupPrivate::Action4Item::action) {
+ act->setVisible(that->isVisible());
+ act->setEnabled(that->isEnabled());
+ }
+ for (QList<Q3ActionGroupPrivate::MenuItem*>::Iterator pu(menuitems.begin()); pu != menuitems.end(); ++pu) {
+ QWidget* parent = (*pu)->popup->parentWidget();
+ if (qobject_cast<Q3PopupMenu*>(parent)) {
+ Q3PopupMenu* ppopup = (Q3PopupMenu*)parent;
+ ppopup->setItemEnabled((*pu)->id, that->isEnabled());
+ ppopup->setItemVisible((*pu)->id, that->isVisible());
+ } else {
+ (*pu)->popup->setEnabled(that->isEnabled());
+ }
+ }
+ for (QList<Q3PopupMenu*>::Iterator pm(popupmenus.begin()); pm != popupmenus.end(); ++pm) {
+ Q3PopupMenu *popup = *pm;
+ Q3PopupMenu *parent = qobject_cast<Q3PopupMenu*>(popup->parentWidget());
+ if (!parent)
+ continue;
+
+ int index;
+ parent->findPopup(popup, &index);
+ int id = parent->idAt(index);
+ if (!that->iconSet().isNull())
+ parent->changeItem(id, that->iconSet(), that->menuText());
+ else
+ parent->changeItem(id, that->menuText());
+ parent->setItemEnabled(id, that->isEnabled());
+#ifndef QT_NO_ACCEL
+ parent->setAccel(that->accel(), id);
+#endif
+ }
+}
+
+/*!
+ \class Q3ActionGroup
+ \brief The Q3ActionGroup class groups actions together.
+
+ \compat
+
+ In some situations it is useful to group actions together. For
+ example, if you have a left justify action, a right justify action
+ and a center action, only one of these actions should be active at
+ any one time, and one simple way of achieving this is to group the
+ actions together in an action group.
+
+ An action group can also be added to a menu or a toolbar as a
+ single unit, with all the actions within the action group
+ appearing as separate menu options and toolbar buttons.
+
+ The actions in an action group emit their activated() (and for
+ toggle actions, toggled()) signals as usual.
+
+ The setExclusive() function is used to ensure that only one action
+ is active at any one time: it should be used with actions which
+ have their \c toggleAction set to true.
+
+ Action group actions appear as individual menu options and toolbar
+ buttons. For exclusive action groups use setUsesDropDown() to
+ display the actions in a subwidget of any widget the action group
+ is added to. For example, the actions would appear in a combobox
+ in a toolbar or as a submenu in a menu.
+
+ Actions can be added to an action group using add(), but normally
+ they are added by creating the action with the action group as
+ parent. Actions can have separators dividing them using
+ addSeparator(). Action groups are added to widgets with addTo().
+*/
+
+/*!
+ Constructs an action group called \a name, with parent \a parent.
+
+ The action group is exclusive by default. Call setExclusive(false) to make
+ the action group non-exclusive.
+*/
+Q3ActionGroup::Q3ActionGroup(QObject* parent, const char* name)
+ : Q3Action(parent, name)
+{
+ d = new Q3ActionGroupPrivate;
+ d->exclusive = true;
+ d->dropdown = false;
+ d->selected = 0;
+ d->separatorAction = 0;
+
+ connect(this, SIGNAL(selected(Q3Action*)), SLOT(internalToggle(Q3Action*)));
+}
+
+/*!
+ Constructs an action group called \a name, with parent \a parent.
+
+ If \a exclusive is true only one toggle action in the group will
+ ever be active.
+
+ \sa exclusive
+*/
+Q3ActionGroup::Q3ActionGroup(QObject* parent, const char* name, bool exclusive)
+ : Q3Action(parent, name)
+{
+ d = new Q3ActionGroupPrivate;
+ d->exclusive = exclusive;
+ d->dropdown = false;
+ d->selected = 0;
+ d->separatorAction = 0;
+
+ connect(this, SIGNAL(selected(Q3Action*)), SLOT(internalToggle(Q3Action*)));
+}
+
+/*!
+ Destroys the object and frees allocated resources.
+*/
+
+Q3ActionGroup::~Q3ActionGroup()
+{
+ QList<Q3ActionGroupPrivate::MenuItem*>::Iterator mit(d->menuitems.begin());
+ while (mit != d->menuitems.end()) {
+ Q3ActionGroupPrivate::MenuItem *mi = *mit;
+ ++mit;
+ if (mi->popup)
+ mi->popup->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ }
+
+ QList<QComboBox*>::Iterator cbit(d->comboboxes.begin());
+ while (cbit != d->comboboxes.end()) {
+ QComboBox *cb = *cbit;
+ ++cbit;
+ cb->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ }
+ QList<QToolButton*>::Iterator mbit(d->menubuttons.begin());
+ while (mbit != d->menubuttons.end()) {
+ QToolButton *mb = *mbit;
+ ++mbit;
+ mb->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ }
+ QList<Q3PopupMenu*>::Iterator pmit(d->popupmenus.begin());
+ while (pmit != d->popupmenus.end()) {
+ Q3PopupMenu *pm = *pmit;
+ ++pmit;
+ pm->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
+ }
+
+ QList<Q3ActionGroupPrivate::Action4Item*>::Iterator itmi4(d->action4items.begin());
+ Q3ActionGroupPrivate::Action4Item* mi4;
+ while (itmi4 != d->action4items.end()) {
+ mi4 = *itmi4;
+ ++itmi4;
+ mi4->widget->removeAction(mi4->action);
+ }
+ delete Q3ActionPrivate::Action4Item::action;
+ Q3ActionPrivate::Action4Item::action = 0;
+
+ delete d->separatorAction;
+ while (!d->menubuttons.isEmpty())
+ delete d->menubuttons.takeFirst();
+ while (!d->comboboxes.isEmpty())
+ delete d->comboboxes.takeFirst();
+ while (!d->menuitems.isEmpty())
+ delete d->menuitems.takeFirst();
+ while (!d->popupmenus.isEmpty())
+ delete d->popupmenus.takeFirst();
+ delete d;
+}
+
+/*!
+ \property Q3ActionGroup::exclusive
+ \brief whether the action group does exclusive toggling
+
+ If exclusive is true only one toggle action in the action group
+ can ever be active at any one time. If the user chooses another
+ toggle action in the group the one they chose becomes active and
+ the one that was active becomes inactive.
+
+ \sa Q3Action::toggleAction
+*/
+void Q3ActionGroup::setExclusive(bool enable)
+{
+ d->exclusive = enable;
+}
+
+bool Q3ActionGroup::isExclusive() const
+{
+ return d->exclusive;
+}
+
+/*!
+ \property Q3ActionGroup::usesDropDown
+ \brief whether the group's actions are displayed in a subwidget of
+ the widgets the action group is added to
+
+ Exclusive action groups added to a toolbar display their actions
+ in a combobox with the action's \l Q3Action::text and \l
+ Q3Action::iconSet properties shown. Non-exclusive groups are
+ represented by a tool button showing their \l Q3Action::iconSet and
+ text() property.
+
+ In a popup menu the member actions are displayed in a submenu.
+
+ Changing usesDropDown only affects \e subsequent calls to addTo().
+
+ This property's default is false.
+
+*/
+void Q3ActionGroup::setUsesDropDown(bool enable)
+{
+ d->dropdown = enable;
+}
+
+bool Q3ActionGroup::usesDropDown() const
+{
+ return d->dropdown;
+}
+
+/*!
+ Adds action \a action to this group.
+
+ Normally an action is added to a group by creating it with the
+ group as parent, so this function is not usually used.
+
+ \sa addTo()
+*/
+void Q3ActionGroup::add(Q3Action* action)
+{
+ if (d->actions.contains(action))
+ return;
+
+ d->actions.append(action);
+
+ if (action->whatsThis().isNull())
+ action->setWhatsThis(whatsThis());
+ if (action->toolTip().isNull())
+ action->setToolTip(toolTip());
+ if (!action->d->forceDisabled)
+ action->d->enabled = isEnabled();
+ if (!action->d->forceInvisible)
+ action->d->visible = isVisible();
+
+ connect(action, SIGNAL(destroyed()), this, SLOT(childDestroyed()));
+ connect(action, SIGNAL(activated()), this, SIGNAL(activated()));
+ connect(action, SIGNAL(toggled(bool)), this, SLOT(childToggled(bool)));
+ connect(action, SIGNAL(activated()), this, SLOT(childActivated()));
+
+ for (QList<QComboBox*>::Iterator cb(d->comboboxes.begin()); cb != d->comboboxes.end(); ++cb)
+ action->addTo(*cb);
+ for (QList<QToolButton*>::Iterator mb(d->menubuttons.begin()); mb != d->menubuttons.end(); ++mb) {
+ QMenu* menu = (*mb)->popup();
+ if (!menu)
+ continue;
+ action->addTo(menu);
+ }
+ for (QList<Q3ActionGroupPrivate::Action4Item*>::Iterator ac(d->action4items.begin());
+ ac != d->action4items.end(); ++ac)
+ action->addTo((*ac)->action->menu());
+ for (QList<Q3ActionGroupPrivate::MenuItem*>::Iterator mi(d->menuitems.begin());
+ mi != d->menuitems.end(); ++mi) {
+ Q3PopupMenu* popup = (*mi)->popup;
+ if (!popup)
+ continue;
+ action->addTo(popup);
+ }
+}
+
+/*!
+ Adds a separator to the group.
+*/
+void Q3ActionGroup::addSeparator()
+{
+ if (!d->separatorAction)
+ d->separatorAction = new Q3Action(0, "qt_separator_action");
+ d->actions.append(d->separatorAction);
+}
+
+
+/*!
+ Adds this action group to the widget \a w.
+
+ If isExclusive() is false or usesDropDown() is false, the actions within
+ the group are added to the widget individually. For example, if the widget
+ is a menu, the actions will appear as individual menu options, and
+ if the widget is a toolbar, the actions will appear as toolbar buttons.
+
+ If both isExclusive() and usesDropDown() are true, the actions
+ are presented either in a combobox (if \a w is a toolbar) or in a
+ submenu (if \a w is a menu).
+
+ All actions should be added to the action group \e before the
+ action group is added to the widget. If actions are added to the
+ action group \e after the action group has been added to the
+ widget these later actions will \e not appear.
+
+ \sa setExclusive() setUsesDropDown() removeFrom()
+*/
+bool Q3ActionGroup::addTo(QWidget *w)
+{
+#ifndef QT_NO_TOOLBAR
+ if (qobject_cast<Q3ToolBar*>(w)) {
+ if (d->dropdown) {
+ if (!d->exclusive) {
+ QList<Q3Action*>::Iterator it(d->actions.begin());
+ if (it == d->actions.end() || !(*it))
+ return true;
+
+ Q3Action *defAction = *it;
+
+ QToolButton* btn = new QToolButton((Q3ToolBar*) w, "qt_actiongroup_btn");
+ addedTo(btn, w);
+ connect(btn, SIGNAL(destroyed()), SLOT(objectDestroyed()));
+ d->menubuttons.append(btn);
+
+ if (!iconSet().isNull())
+ btn->setIconSet(iconSet());
+ else if (!defAction->iconSet().isNull())
+ btn->setIconSet(defAction->iconSet());
+ if (text().size())
+ btn->setTextLabel(text());
+ else if (defAction->text().size())
+ btn->setTextLabel(defAction->text());
+#ifndef QT_NO_TOOLTIP
+ if (toolTip().size())
+ QToolTip::add(btn, toolTip());
+ else if (defAction->toolTip().size())
+ QToolTip::add(btn, defAction->toolTip());
+#endif
+#ifndef QT_NO_WHATSTHIS
+ if (whatsThis().size())
+ QWhatsThis::add(btn, whatsThis());
+ else if (defAction->whatsThis().size())
+ QWhatsThis::add(btn, defAction->whatsThis());
+#endif
+
+ connect(btn, SIGNAL(clicked()), defAction, SIGNAL(activated()));
+ connect(btn, SIGNAL(toggled(bool)), defAction, SLOT(toolButtonToggled(bool)));
+ connect(btn, SIGNAL(destroyed()), defAction, SLOT(objectDestroyed()));
+
+ Q3PopupMenu *menu = new Q3PopupMenu(btn, "qt_actiongroup_menu");
+ btn->setPopupDelay(0);
+ btn->setPopup(menu);
+ btn->setPopupMode(QToolButton::MenuButtonPopup);
+
+ while (it != d->actions.end()) {
+ (*it)->addTo(menu);
+ ++it;
+ }
+ d->update(this);
+ return true;
+ } else {
+ QComboBox *box = new QComboBox(false, w, "qt_actiongroup_combo");
+ addedTo(box, w);
+ connect(box, SIGNAL(destroyed()), SLOT(objectDestroyed()));
+ d->comboboxes.append(box);
+#ifndef QT_NO_TOOLTIP
+ if (toolTip().size())
+ QToolTip::add(box, toolTip());
+#endif
+#ifndef QT_NO_WHATSTHIS
+ if (whatsThis().size())
+ QWhatsThis::add(box, whatsThis());
+#endif
+ int onIndex = 0;
+ bool foundOn = false;
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ Q3Action *action = *it;
+ if (!foundOn)
+ foundOn = action->isOn();
+ if (action->objectName() != QLatin1String("qt_separator_action") && !foundOn)
+ onIndex++;
+ action->addTo(box);
+ }
+ if (foundOn)
+ box->setCurrentItem(onIndex);
+ connect(box, SIGNAL(activated(int)), this, SLOT(internalComboBoxActivated(int)));
+ connect(box, SIGNAL(highlighted(int)), this, SLOT(internalComboBoxHighlighted(int)));
+ d->update(this);
+ return true;
+ }
+ }
+ } else
+#endif
+ if (qobject_cast<Q3PopupMenu*>(w)) {
+ Q3PopupMenu *popup;
+ if (d->dropdown) {
+ Q3PopupMenu *menu = (Q3PopupMenu*)w;
+ popup = new Q3PopupMenu(w, "qt_actiongroup_menu");
+ d->popupmenus.append(popup);
+ connect(popup, SIGNAL(destroyed()), SLOT(objectDestroyed()));
+
+ int id;
+ if (!iconSet().isNull()) {
+ if (menuText().isEmpty())
+ id = menu->insertItem(iconSet(), text(), popup);
+ else
+ id = menu->insertItem(iconSet(), menuText(), popup);
+ } else {
+ if (menuText().isEmpty())
+ id = menu->insertItem(text(), popup);
+ else
+ id = menu->insertItem(menuText(), popup);
+ }
+
+ addedTo(menu->indexOf(id), menu);
+
+ Q3ActionGroupPrivate::MenuItem *item = new Q3ActionGroupPrivate::MenuItem;
+ item->id = id;
+ item->popup = popup;
+ d->menuitems.append(item);
+ } else {
+ popup = (Q3PopupMenu*)w;
+ }
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ // #### do an addedTo(index, popup, action), need to find out index
+ (*it)->addTo(popup);
+ }
+ return true;
+ }
+ if (qobject_cast<QMenu*>(w)) {
+ QMenu *menu = (QMenu*)w;
+ if (d->dropdown) {
+ Q3ActionGroupPrivate::Action4Item *ai = new Q3ActionGroupPrivate::Action4Item;
+ if(!ai->action) { //static
+ ai->action = menu->menuAction();
+ if (!iconSet().isNull())
+ ai->action->setIcon(iconSet());
+ if (menuText().isEmpty())
+ ai->action->setText(text());
+ else
+ ai->action->setText(menuText());
+ }
+ addedTo(w, w);
+ ai->widget = w;
+ ai->widget->addAction(Q3ActionGroupPrivate::Action4Item::action);
+ d->action4items.append(ai);
+ menu = ai->action->menu();
+ }
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it)
+ (*it)->addTo(menu);
+ return true;
+ }
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ // #### do an addedTo(index, popup, action), need to find out index
+ (*it)->addTo(w);
+ }
+ return true;
+}
+
+/*! \reimp
+*/
+bool Q3ActionGroup::removeFrom(QWidget* w)
+{
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it)
+ (*it)->removeFrom(w);
+
+#ifndef QT_NO_TOOLBAR
+ if (qobject_cast<Q3ToolBar*>(w)) {
+ QList<QComboBox*>::Iterator cb(d->comboboxes.begin());
+ while (cb != d->comboboxes.end()) {
+ QComboBox *box = *cb;
+ ++cb;
+ if (box->parentWidget() == w)
+ delete box;
+ }
+ QList<QToolButton*>::Iterator mb(d->menubuttons.begin());
+ while (mb != d->menubuttons.end()) {
+ QToolButton *btn = *mb;
+ ++mb;
+ if (btn->parentWidget() == w)
+ delete btn;
+ }
+ } else
+#endif
+ if (qobject_cast<Q3PopupMenu*>(w)) {
+ QList<Q3ActionGroupPrivate::MenuItem*>::Iterator pu(d->menuitems.begin());
+ while (pu != d->menuitems.end()) {
+ Q3ActionGroupPrivate::MenuItem *mi = *pu;
+ ++pu;
+ if (d->dropdown && mi->popup)
+ ((Q3PopupMenu*)w)->removeItem(mi->id);
+ delete mi->popup;
+ }
+ }
+ if (qobject_cast<QMenu*>(w)) {
+ QList<Q3ActionGroupPrivate::Action4Item*>::Iterator it(d->action4items.begin());
+ Q3ActionGroupPrivate::Action4Item *a4i;
+ while (it != d->action4items.end()) {
+ a4i = *it;
+ ++it;
+ if (a4i->widget == w) {
+ a4i->widget->removeAction(a4i->action);
+ d->action4items.removeAll(a4i);
+ delete a4i;
+ }
+ }
+ }
+ return true;
+}
+
+/*! \internal
+*/
+void Q3ActionGroup::childToggled(bool b)
+{
+ if (!isExclusive())
+ return;
+ Q3Action* s = qobject_cast<Q3Action*>(sender());
+ if (!s)
+ return;
+ if (b) {
+ if (s != d->selected) {
+ d->selected = s;
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ if ((*it)->isToggleAction() && (*it) != s)
+ (*it)->setOn(false);
+ }
+ emit selected(s);
+ }
+ } else {
+ if (s == d->selected) {
+ // at least one has to be selected
+ s->setOn(true);
+ }
+ }
+}
+
+/*! \internal
+*/
+void Q3ActionGroup::childActivated()
+{
+ Q3Action* s = qobject_cast<Q3Action*>(sender());
+ if (s) {
+ emit activated(s);
+ emit Q3Action::activated();
+ }
+}
+
+
+/*! \internal
+*/
+void Q3ActionGroup::childDestroyed()
+{
+ d->actions.removeAll((Q3Action *)sender());
+ if (d->selected == sender())
+ d->selected = 0;
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setEnabled(bool enable)
+{
+ if (enable == isEnabled())
+ return;
+ Q3Action::setEnabled(enable);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setToggleAction(bool toggle)
+{
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it)
+ (*it)->setToggleAction(toggle);
+ Q3Action::setToggleAction(true);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setOn(bool on)
+{
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ Q3Action *act = *it;
+ if (act->isToggleAction())
+ act->setOn(on);
+ }
+ Q3Action::setOn(on);
+ d->update(this);
+}
+
+/*! \reimp
+ */
+void Q3ActionGroup::setVisible(bool visible)
+{
+ Q3Action::setVisible(visible);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setIconSet(const QIcon& icon)
+{
+ Q3Action::setIconSet(icon);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setText(const QString& txt)
+{
+ if (txt == text())
+ return;
+
+ Q3Action::setText(txt);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setMenuText(const QString& text)
+{
+ if (text == menuText())
+ return;
+
+ Q3Action::setMenuText(text);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setToolTip(const QString& text)
+{
+ if (text == toolTip())
+ return;
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ if ((*it)->toolTip().isNull())
+ (*it)->setToolTip(text);
+ }
+ Q3Action::setToolTip(text);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::setWhatsThis(const QString& text)
+{
+ if (text == whatsThis())
+ return;
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ if ((*it)->whatsThis().isNull())
+ (*it)->setWhatsThis(text);
+ }
+ Q3Action::setWhatsThis(text);
+ d->update(this);
+}
+
+/*! \reimp
+*/
+void Q3ActionGroup::childEvent(QChildEvent *e)
+{
+ if (!e->removed())
+ return;
+
+ Q3Action *action = qobject_cast<Q3Action*>(e->child());
+ if (!action)
+ return;
+
+ for (QList<QComboBox*>::Iterator cb(d->comboboxes.begin());
+ cb != d->comboboxes.end(); ++cb) {
+ for (int i = 0; i < (*cb)->count(); i++) {
+ if ((*cb)->text(i) == action->text()) {
+ (*cb)->removeItem(i);
+ break;
+ }
+ }
+ }
+ for (QList<QToolButton*>::Iterator mb(d->menubuttons.begin());
+ mb != d->menubuttons.end(); ++mb) {
+ QMenu* popup = (*mb)->popup();
+ if (!popup)
+ continue;
+ action->removeFrom(popup);
+ }
+ for (QList<Q3ActionGroupPrivate::MenuItem*>::Iterator mi(d->menuitems.begin());
+ mi != d->menuitems.end(); ++mi) {
+ Q3PopupMenu* popup = (*mi)->popup;
+ if (!popup)
+ continue;
+ action->removeFrom(popup);
+ }
+ if(QAction *act = Q3ActionGroupPrivate::Action4Item::action)
+ action->removeFrom(act->menu());
+}
+
+/*!
+ \fn void Q3ActionGroup::selected(Q3Action* action)
+
+ This signal is emitted from exclusive groups when toggle actions
+ change state.
+
+ The argument is the \a action whose state changed to "on".
+
+ \sa setExclusive(), isOn() Q3Action::toggled()
+*/
+
+/*!
+ \fn void Q3ActionGroup::activated(Q3Action* action)
+
+ This signal is emitted from groups when one of its actions gets
+ activated.
+
+ The argument is the \a action which was activated.
+
+ \sa setExclusive(), isOn() Q3Action::toggled()
+*/
+
+
+/*! \internal
+*/
+void Q3ActionGroup::internalComboBoxActivated(int index)
+{
+ if (index == -1)
+ return;
+
+ Q3Action *a = 0;
+ for (int i = 0; i <= index && i < (int)d->actions.count(); ++i) {
+ a = d->actions.at(i);
+ if (a && a->objectName() == QLatin1String("qt_separator_action"))
+ index++;
+ }
+ a = d->actions.at(index);
+ if (a) {
+ if (a != d->selected) {
+ d->selected = a;
+ for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) {
+ if ((*it)->isToggleAction() && (*it) != a)
+ (*it)->setOn(false);
+ }
+ if (a->isToggleAction())
+ a->setOn(true);
+
+ emit activated(a);
+ emit Q3Action::activated();
+ emit a->activated();
+ if (a->isToggleAction())
+ emit selected(d->selected);
+ } else if (!a->isToggleAction()) {
+ emit activated(a);
+ emit Q3Action::activated();
+ emit a->activated();
+ }
+ a->clearStatusText();
+ }
+}
+
+/*! \internal
+*/
+void Q3ActionGroup::internalComboBoxHighlighted(int index)
+{
+ Q3Action *a = 0;
+ for (int i = 0; i <= index && i < (int)d->actions.count(); ++i) {
+ a = d->actions.at(i);
+ if (a && a->objectName() == QLatin1String("qt_separator_action"))
+ index++;
+ }
+ a = d->actions.at(index);
+ if (a)
+ a->showStatusText(a->statusTip());
+ else
+ clearStatusText();
+}
+
+/*! \internal
+*/
+void Q3ActionGroup::internalToggle(Q3Action *a)
+{
+ int index = d->actions.indexOf(a);
+ if (index == -1)
+ return;
+
+ int lastItem = index;
+ for (int i=0; i<lastItem; ++i) {
+ Q3Action *action = d->actions.at(i);
+ if (action->objectName() == QLatin1String("qt_separator_action"))
+ --index;
+ }
+
+ for (QList<QComboBox*>::Iterator it(d->comboboxes.begin());
+ it != d->comboboxes.end(); ++it)
+ (*it)->setCurrentItem(index);
+}
+
+/*! \internal
+*/
+void Q3ActionGroup::objectDestroyed()
+{
+ const QObject* obj = sender();
+ d->menubuttons.removeAll((QToolButton *)obj);
+ for (QList<Q3ActionGroupPrivate::MenuItem *>::Iterator mi(d->menuitems.begin());
+ mi != d->menuitems.end(); ++mi) {
+ if ((*mi)->popup == obj) {
+ d->menuitems.removeAll(*mi);
+ delete *mi;
+ break;
+ }
+ }
+ d->popupmenus.removeAll((Q3PopupMenu*)obj);
+ d->comboboxes.removeAll((QComboBox*)obj);
+}
+
+/*!
+ This function is called from the addTo() function when it has
+ created a widget (\a actionWidget) for the child action \a a in
+ the \a container.
+*/
+
+void Q3ActionGroup::addedTo(QWidget *actionWidget, QWidget *container, Q3Action *a)
+{
+ Q_UNUSED(actionWidget);
+ Q_UNUSED(container);
+ Q_UNUSED(a);
+}
+
+/*!
+ \overload
+
+ This function is called from the addTo() function when it has
+ created a menu item for the child action at the index position \a
+ index in the popup menu \a menu.
+*/
+
+void Q3ActionGroup::addedTo(int index, Q3PopupMenu *menu, Q3Action *a)
+{
+ Q_UNUSED(index);
+ Q_UNUSED(menu);
+ Q_UNUSED(a);
+}
+
+/*!
+ \reimp
+ \overload
+
+ This function is called from the addTo() function when it has
+ created a widget (\a actionWidget) in the \a container.
+*/
+
+void Q3ActionGroup::addedTo(QWidget *actionWidget, QWidget *container)
+{
+ Q_UNUSED(actionWidget);
+ Q_UNUSED(container);
+}
+
+/*!
+ \reimp
+ \overload
+
+ This function is called from the addTo() function when it has
+ created a menu item at the index position \a index in the popup
+ menu \a menu.
+*/
+
+void Q3ActionGroup::addedTo(int index, Q3PopupMenu *menu)
+{
+ Q_UNUSED(index);
+ Q_UNUSED(menu);
+}
+
+/*!
+ \fn void Q3ActionGroup::insert(Q3Action *action)
+
+ Use add(\a action) instead.
+*/
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/widgets/q3action.h b/src/qt3support/widgets/q3action.h
new file mode 100644
index 0000000..58c15b0
--- /dev/null
+++ b/src/qt3support/widgets/q3action.h
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3ACTION_H
+#define Q3ACTION_H
+
+#include <QtGui/qicon.h>
+#include <QtGui/qkeysequence.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_ACTION
+
+class Q3ActionPrivate;
+class Q3ActionGroupPrivate;
+class QStatusBar;
+class Q3PopupMenu;
+class QToolTipGroup;
+class QWidget;
+
+class Q_COMPAT_EXPORT Q3Action : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool toggleAction READ isToggleAction WRITE setToggleAction)
+ Q_PROPERTY(bool on READ isOn WRITE setOn)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+ Q_PROPERTY(QIcon iconSet READ iconSet WRITE setIconSet)
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QString menuText READ menuText WRITE setMenuText)
+ Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip)
+ Q_PROPERTY(QString statusTip READ statusTip WRITE setStatusTip)
+ Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis)
+#ifndef QT_NO_ACCEL
+ Q_PROPERTY(QKeySequence accel READ accel WRITE setAccel)
+#endif
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
+
+public:
+ Q3Action(QObject* parent, const char* name = 0);
+#ifndef QT_NO_ACCEL
+ Q3Action(const QString& menuText, QKeySequence accel,
+ QObject* parent, const char* name = 0);
+ Q3Action(const QIcon& icon, const QString& menuText, QKeySequence accel,
+ QObject* parent, const char* name = 0);
+
+ Q3Action(const QString& text, const QIcon& icon, const QString& menuText, QKeySequence accel,
+ QObject* parent, const char* name = 0, bool toggle = false); // obsolete
+ Q3Action(const QString& text, const QString& menuText, QKeySequence accel, QObject* parent,
+ const char* name = 0, bool toggle = false); // obsolete
+#endif
+ Q3Action(QObject* parent, const char* name , bool toggle); // obsolete
+ ~Q3Action();
+
+ virtual void setIconSet(const QIcon&);
+ QIcon iconSet() const;
+ virtual void setText(const QString&);
+ QString text() const;
+ virtual void setMenuText(const QString&);
+ QString menuText() const;
+ virtual void setToolTip(const QString&);
+ QString toolTip() const;
+ virtual void setStatusTip(const QString&);
+ QString statusTip() const;
+ virtual void setWhatsThis(const QString&);
+ QString whatsThis() const;
+#ifndef QT_NO_ACCEL
+ virtual void setAccel(const QKeySequence& key);
+ QKeySequence accel() const;
+#endif
+ virtual void setToggleAction(bool);
+
+ bool isToggleAction() const;
+ bool isOn() const;
+ bool isEnabled() const;
+ bool isVisible() const;
+ virtual bool addTo(QWidget*);
+ virtual bool removeFrom(QWidget*);
+
+protected:
+ virtual void addedTo(QWidget *actionWidget, QWidget *container);
+ virtual void addedTo(int index, Q3PopupMenu *menu);
+
+public Q_SLOTS:
+ void activate();
+ void toggle();
+ virtual void setOn(bool);
+ virtual void setEnabled(bool);
+ void setDisabled(bool);
+ virtual void setVisible(bool);
+
+Q_SIGNALS:
+ void activated();
+ void toggled(bool);
+
+private Q_SLOTS:
+ void internalActivation();
+ void toolButtonToggled(bool);
+ void objectDestroyed();
+ void menuStatusText(int id);
+ void showStatusText(const QString&);
+ void clearStatusText();
+
+private:
+ Q_DISABLE_COPY(Q3Action)
+
+ void init();
+
+ Q3ActionPrivate* d;
+
+ friend class Q3ActionPrivate;
+ friend class Q3ActionGroup;
+ friend class Q3ActionGroupPrivate;
+};
+
+class Q_COMPAT_EXPORT Q3ActionGroup : public Q3Action
+{
+ Q_OBJECT
+ Q_PROPERTY(bool exclusive READ isExclusive WRITE setExclusive)
+ Q_PROPERTY(bool usesDropDown READ usesDropDown WRITE setUsesDropDown)
+
+public:
+ Q3ActionGroup(QObject* parent, const char* name = 0);
+ Q3ActionGroup(QObject* parent, const char* name , bool exclusive ); // obsolete
+ ~Q3ActionGroup();
+ void setExclusive(bool);
+ bool isExclusive() const;
+ void add(Q3Action* a);
+ void addSeparator();
+ bool addTo(QWidget*);
+ bool removeFrom(QWidget*);
+ void setEnabled(bool);
+ void setToggleAction(bool toggle);
+ void setOn(bool on);
+ void setVisible(bool);
+
+ void setUsesDropDown(bool enable);
+ bool usesDropDown() const;
+
+ void setIconSet(const QIcon &);
+ void setText(const QString&);
+ void setMenuText(const QString&);
+ void setToolTip(const QString&);
+ void setWhatsThis(const QString&);
+
+protected:
+ void childEvent(QChildEvent*);
+ virtual void addedTo(QWidget *actionWidget, QWidget *container, Q3Action *a);
+ virtual void addedTo(int index, Q3PopupMenu *menu, Q3Action *a);
+ virtual void addedTo(QWidget *actionWidget, QWidget *container);
+ virtual void addedTo(int index, Q3PopupMenu *menu);
+
+Q_SIGNALS:
+ void selected(Q3Action*);
+ void activated(Q3Action *);
+
+private Q_SLOTS:
+ void childToggled(bool);
+ void childActivated();
+ void childDestroyed();
+ void internalComboBoxActivated(int);
+ void internalComboBoxHighlighted(int);
+ void internalToggle(Q3Action*);
+ void objectDestroyed();
+
+private:
+ Q3ActionGroupPrivate* d;
+
+public:
+ void insert(Q3Action *a) { add(a); }
+
+private:
+ Q_DISABLE_COPY(Q3ActionGroup)
+};
+
+#endif // QT_NO_ACTION
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3ACTION_H
diff --git a/src/qt3support/widgets/q3button.cpp b/src/qt3support/widgets/q3button.cpp
new file mode 100644
index 0000000..65ca16b
--- /dev/null
+++ b/src/qt3support/widgets/q3button.cpp
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3button.h"
+#include "qpainter.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3Button
+ \brief The Q3Button class is a compatibility base class of button
+ widgets
+
+ \compat
+
+ \bold{In new code, use QAbstractButton.}
+
+ To subclass Q3Button, you must reimplement at least drawButton()
+ (to draw the button's outline) and drawButtonLabel() (to draw its
+ text or pixmap). It is generally advisable to reimplement
+ sizeHint() as well, and sometimes hitButton() (to determine
+ whether a button press is within the button).
+*/
+
+/*!
+ Constructs a standard button called \a name with parent \a parent,
+ using the widget flags \a f.
+*/
+
+Q3Button::Q3Button( QWidget *parent, const char *name, Qt::WindowFlags f )
+ : QAbstractButton( parent, name, f )
+{
+}
+
+/*!
+ Destroys the button.
+ */
+Q3Button::~Q3Button()
+{
+}
+
+/*!
+ \fn void Q3Button::paintEvent( QPaintEvent *event)
+
+ Handles paint events, received in \a event, for buttons. Small and
+ typically complex buttons are painted double-buffered to reduce
+ flicker. The actually drawing is done in the virtual functions
+ drawButton() and drawButtonLabel().
+
+ \sa drawButton(), drawButtonLabel()
+*/
+void Q3Button::paintEvent( QPaintEvent *)
+{
+ QPainter p(this);
+ drawButton( &p );
+}
+
+/*!
+ \fn void Q3Button::drawButton( QPainter *painter)
+
+ Draws the button on the given \a painter. The default
+ implementation does nothing.
+
+ This virtual function is reimplemented by subclasses to draw real
+ buttons. At some point, these reimplementations should call
+ drawButtonLabel().
+
+ \sa drawButtonLabel(), paintEvent()
+*/
+void Q3Button::drawButton( QPainter * )
+{
+}
+
+/*!
+ \fn void Q3Button::drawButtonLabel( QPainter *painter )
+
+ Draws the button text or pixmap on the given \a painter.
+
+ This virtual function is reimplemented by subclasses to draw real
+ buttons. It is invoked by drawButton().
+
+ \sa drawButton(), paintEvent()
+*/
+
+void Q3Button::drawButtonLabel( QPainter * )
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3button.h b/src/qt3support/widgets/q3button.h
new file mode 100644
index 0000000..699585c
--- /dev/null
+++ b/src/qt3support/widgets/q3button.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3BUTTON_H
+#define Q3BUTTON_H
+
+#include <QtGui/qabstractbutton.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3Button : public QAbstractButton
+{
+ Q_OBJECT
+public:
+ Q3Button( QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0 );
+ ~Q3Button();
+
+protected:
+ virtual void drawButton( QPainter * );
+ virtual void drawButtonLabel( QPainter * );
+ void paintEvent( QPaintEvent * );
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3BUTTON_H
diff --git a/src/qt3support/widgets/q3buttongroup.cpp b/src/qt3support/widgets/q3buttongroup.cpp
new file mode 100644
index 0000000..7349a79
--- /dev/null
+++ b/src/qt3support/widgets/q3buttongroup.cpp
@@ -0,0 +1,565 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3buttongroup.h"
+#include "qabstractbutton.h"
+#include "qmap.h"
+#include "qapplication.h"
+#include "qradiobutton.h"
+#include "qevent.h"
+#include "qset.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3ButtonGroup
+ \brief The Q3ButtonGroup widget organizes QAbstractButton widgets in a group.
+
+ \compat
+
+ A button group widget makes it easier to deal with groups of
+ buttons. Each button in a button group has a unique identifier.
+ The button group emits a clicked() signal with this identifier
+ when a button in the group is clicked. This makes a button group
+ particularly useful when you have several similar buttons and want
+ to connect all their clicked() signals to a single slot.
+
+ An \link setExclusive() exclusive\endlink button group switches
+ off all toggle buttons except the one that was clicked. A button
+ group is, by default, non-exclusive. Note that all radio buttons
+ that are inserted into a button group are mutually exclusive even
+ if the button group is non-exclusive. (See
+ setRadioButtonExclusive().)
+
+ There are two ways of using a button group:
+ \list
+ \i The button group is the parent widget of a number of buttons,
+ i.e. the button group is the parent argument in the button
+ constructor. The buttons are assigned identifiers 0, 1, 2, etc.,
+ in the order they are created. A Q3ButtonGroup can display a frame
+ and a title because it inherits Q3GroupBox.
+ \i The button group is an invisible widget and the contained
+ buttons have some other parent widget. In this usage, each button
+ must be manually inserted, using insert(), into the button group
+ and given an identifier.
+ \endlist
+
+ A button can be removed from the group with remove(). A pointer to
+ a button with a given id can be obtained using find(). The id of a
+ button is available using id(). A button can be set \e on with
+ setButton(). The number of buttons in the group is returned by
+ count().
+
+ \sa QPushButton, QCheckBox, QRadioButton
+*/
+
+/*!
+ \property Q3ButtonGroup::exclusive
+ \brief whether the button group is exclusive
+
+ If this property is true, then the buttons in the group are
+ toggled, and to untoggle a button you must click on another button
+ in the group. The default value is false.
+*/
+
+/*!
+ \property Q3ButtonGroup::radioButtonExclusive
+ \brief whether the radio buttons in the group are exclusive
+
+ If this property is true (the default), the \link QRadioButton
+ radio buttons\endlink in the group are treated exclusively.
+*/
+
+
+/*!
+ Constructs a button group with no title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3ButtonGroup::Q3ButtonGroup(QWidget *parent, const char *name)
+ : Q3GroupBox(parent, name)
+{
+ init();
+}
+
+/*!
+ Constructs a button group with the title \a title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3ButtonGroup::Q3ButtonGroup(const QString &title, QWidget *parent,
+ const char *name)
+ : Q3GroupBox(title, parent, name)
+{
+ init();
+}
+
+/*!
+ Constructs a button group with no title. Child widgets will be
+ arranged in \a strips rows or columns (depending on \a
+ orientation).
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3ButtonGroup::Q3ButtonGroup(int strips, Qt::Orientation orientation,
+ QWidget *parent, const char *name)
+ : Q3GroupBox(strips, orientation, parent, name)
+{
+ init();
+}
+
+/*!
+ Constructs a button group with title \a title. Child widgets will
+ be arranged in \a strips rows or columns (depending on \a
+ orientation).
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3ButtonGroup::Q3ButtonGroup(int strips, Qt::Orientation orientation,
+ const QString &title, QWidget *parent,
+ const char *name)
+ : Q3GroupBox(strips, orientation, title, parent, name)
+{
+ init();
+}
+
+/*!
+ Initializes the button group.
+*/
+
+void Q3ButtonGroup::init()
+{
+ group.setExclusive(false);
+ radio_excl = true;
+}
+
+/*! Destructor. */
+
+Q3ButtonGroup::~Q3ButtonGroup()
+{
+}
+
+bool Q3ButtonGroup::isExclusive() const
+{
+ return group.exclusive();
+}
+
+void Q3ButtonGroup::setExclusive(bool enable)
+{
+ group.setExclusive(enable);
+}
+
+
+/*!
+ Inserts the \a button with the identifier \a id into the button
+ group. Returns the button identifier.
+
+ Buttons are normally inserted into a button group automatically by
+ passing the button group as the parent when the button is
+ constructed. So it is not necessary to manually insert buttons
+ that have this button group as their parent widget. An exception
+ is when you want custom identifiers instead of the default 0, 1,
+ 2, etc., or if you want the buttons to have some other parent.
+
+ The button is assigned the identifier \a id or an automatically
+ generated identifier. It works as follows: If \a id >= 0, this
+ identifier is assigned. If \a id == -1 (default), the identifier
+ is equal to the number of buttons in the group. If \a id is any
+ other negative integer, for instance -2, a unique identifier
+ (negative integer \<= -2) is generated. No button has an id of -1.
+
+ \sa find(), remove(), setExclusive()
+*/
+
+int Q3ButtonGroup::insert(QAbstractButton *button, int id)
+{
+ remove_helper(button);
+ return insert_helper(button, id);
+}
+
+int Q3ButtonGroup::insert_helper(QAbstractButton *button, int id)
+{
+ if (isExclusive() || !qobject_cast<QRadioButton*>(button))
+ group.addButton(button);
+
+ static int seq_no = -2;
+ if (id < -1)
+ id = seq_no--;
+ else if (id == -1)
+ id = buttonIds.count();
+ buttonIds.insert(id, button);
+ connect(button, SIGNAL(pressed()) , SLOT(buttonPressed()));
+ connect(button, SIGNAL(released()), SLOT(buttonReleased()));
+ connect(button, SIGNAL(clicked()) , SLOT(buttonClicked()));
+ connect(button, SIGNAL(destroyed()) , SLOT(buttonDestroyed()));
+ return id;
+}
+
+/*!
+ Returns the number of buttons in the group.
+*/
+int Q3ButtonGroup::count() const
+{
+ fixChildren();
+ return buttonIds.count();
+}
+
+/*!
+ Removes the \a button from the button group.
+
+ \sa insert()
+*/
+
+void Q3ButtonGroup::remove(QAbstractButton *button)
+{
+ fixChildren();
+ remove_helper(button);
+}
+
+void Q3ButtonGroup::remove_helper(QAbstractButton *button)
+{
+ QMap<int, QAbstractButton*>::Iterator it = buttonIds.begin();
+ while (it != buttonIds.end()) {
+ if (it.value() == button) {
+ buttonIds.erase(it);
+ button->disconnect(this);
+ group.removeButton(button);
+ break;
+ }
+ ++it;
+ }
+}
+
+
+/*!
+ Returns the button with the specified identifier \a id, or 0 if
+ the button was not found.
+*/
+
+QAbstractButton *Q3ButtonGroup::find(int id) const
+{
+ fixChildren();
+ return buttonIds.value(id);
+}
+
+
+/*!
+ \fn void Q3ButtonGroup::pressed(int id)
+
+ This signal is emitted when a button in the group is \link
+ QAbstractButton::pressed() pressed\endlink. The \a id argument is the
+ button's identifier.
+
+ \sa insert()
+*/
+
+/*!
+ \fn void Q3ButtonGroup::released(int id)
+
+ This signal is emitted when a button in the group is \link
+ QAbstractButton::released() released\endlink. The \a id argument is the
+ button's identifier.
+
+ \sa insert()
+*/
+
+/*!
+ \fn void Q3ButtonGroup::clicked(int id)
+
+ This signal is emitted when a button in the group is \link
+ QAbstractButton::clicked() clicked\endlink. The \a id argument is the
+ button's identifier.
+
+ \sa insert()
+*/
+
+
+/*!
+ \internal
+ This slot is activated when one of the buttons in the group emits the
+ QAbstractButton::pressed() signal.
+*/
+
+void Q3ButtonGroup::buttonPressed()
+{
+ QAbstractButton *senderButton = qobject_cast<QAbstractButton *>(sender());
+ Q_ASSERT(senderButton);
+ int senderId = id(senderButton);
+ if (senderId != -1)
+ emit pressed(senderId);
+}
+
+/*!
+ \internal
+ This slot is activated when one of the buttons in the group emits the
+ QAbstractButton::released() signal.
+*/
+
+void Q3ButtonGroup::buttonReleased()
+{
+ QAbstractButton *senderButton = qobject_cast<QAbstractButton *>(sender());
+ Q_ASSERT(senderButton);
+ int senderId = id(senderButton);
+ if (senderId != -1)
+ emit released(senderId);
+}
+
+/*!
+ \internal
+ This slot is activated when one of the buttons in the group emits the
+ QAbstractButton::clicked() signal.
+*/
+
+void Q3ButtonGroup::buttonClicked()
+{
+ QAbstractButton *senderButton = qobject_cast<QAbstractButton *>(sender());
+ Q_ASSERT(senderButton);
+ int senderId = id(senderButton);
+ if (senderId != -1)
+ emit clicked(senderId);
+}
+
+/*!
+ \internal
+*/
+void Q3ButtonGroup::buttonDestroyed()
+{
+ int id = buttonIds.key(static_cast<QAbstractButton *>(sender()), -1);
+ if (id != -1)
+ buttonIds.remove(id);
+}
+
+void Q3ButtonGroup::setButton(int id)
+{
+ QAbstractButton *b = find(id);
+ if (b)
+ b->setOn(true);
+}
+
+void Q3ButtonGroup::setRadioButtonExclusive(bool on)
+{
+ radio_excl = on;
+}
+
+
+/*!
+ Returns the selected toggle button if exactly one is selected;
+ otherwise returns 0.
+
+ \sa selectedId()
+*/
+
+QAbstractButton *Q3ButtonGroup::selected() const
+{
+ fixChildren();
+ QAbstractButton *candidate = 0;
+ QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin();
+ while (it != buttonIds.constEnd()) {
+ if (it.value()->isCheckable() && it.value()->isChecked()) {
+ if (candidate)
+ return 0;
+ candidate = it.value();
+ }
+ ++it;
+ }
+ return candidate;
+}
+
+/*!
+ \property Q3ButtonGroup::selectedId
+ \brief The id of the selected toggle button.
+
+ If no toggle button is selected, id() returns -1.
+
+ If setButton() is called on an exclusive group, the button with
+ the given id will be set to on and all the others will be set to
+ off.
+
+ \sa selected()
+*/
+
+int Q3ButtonGroup::selectedId() const
+{
+ return id(selected());
+}
+
+
+/*!
+ Returns the id of \a button, or -1 if \a button is not a member of
+ this group.
+
+ \sa selectedId()
+*/
+
+int Q3ButtonGroup::id(QAbstractButton *button) const
+{
+ fixChildren();
+ QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin();
+ while (it != buttonIds.constEnd()) {
+ if (it.value() == button)
+ return it.key();
+ ++it;
+ }
+ return -1;
+}
+
+
+/*!
+ \reimp
+*/
+bool Q3ButtonGroup::event(QEvent * e)
+{
+ if (e->type() == QEvent::ChildInserted) {
+ QChildEvent * ce = (QChildEvent *) e;
+ if (QAbstractButton *button = qobject_cast<QAbstractButton*>(ce->child())) {
+ button->setAutoExclusive(false);
+ if (group.exclusive() || qobject_cast<QRadioButton*>(button)) {
+ button->setAutoExclusive(true);
+ QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin();
+ while (it != buttonIds.constEnd()) {
+ if (it.value() == button)
+ return Q3GroupBox::event(e);
+ ++it;
+ }
+ }
+ insert(button, id(button));
+ }
+ }
+ return Q3GroupBox::event(e);
+}
+
+void Q3ButtonGroup::fixChildren() const
+{
+ if (children().count() == buttonIds.count())
+ return; // small optimization, all our children have ids.
+
+ QSet<QAbstractButton*> set;
+ for (QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin();
+ it != buttonIds.constEnd(); ++it)
+ set.insert(*it);
+ // Use children() instead of qFindChildren<QAbstractButton*> because the search
+ // should not be recursive.We match with the behavior of Qt3
+ const QObjectList childList = children();
+ Q_FOREACH(QObject* obj, childList) {
+ QAbstractButton *button = qobject_cast<QAbstractButton*>(obj);
+ if ( button && !set.contains(button))
+ const_cast<Q3ButtonGroup*>(this)->insert_helper(button);
+ }
+}
+
+
+/*!
+ \class Q3HButtonGroup
+ \brief The Q3HButtonGroup widget organizes button widgets in a
+ group with one horizontal row.
+
+ \compat
+
+ Q3HButtonGroup is a convenience class that offers a thin layer on
+ top of Q3ButtonGroup. From a layout point of view it is effectively
+ a Q3HBoxWidget that offers a frame with a title and is specifically
+ designed for buttons. From a functionality point of view it is a
+ Q3ButtonGroup.
+
+ \sa Q3VButtonGroup
+*/
+
+/*!
+ \fn Q3HButtonGroup::Q3HButtonGroup(QWidget *parent, const char *name)
+
+ Constructs a horizontal button group with no title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+/*!
+ \fn Q3HButtonGroup::Q3HButtonGroup(const QString &title, QWidget *parent,
+ const char *name)
+
+ Constructs a horizontal button group with the title \a title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+/*!
+ \class Q3VButtonGroup
+ \brief The Q3VButtonGroup widget organizes button widgets in a
+ vertical column.
+
+ \compat
+
+ Q3VButtonGroup is a convenience class that offers a thin layer on top
+ of Q3ButtonGroup. Think of it as a QVBoxWidget that offers a frame with a
+ title and is specifically designed for buttons.
+
+ \sa Q3HButtonGroup
+*/
+
+/*!
+ \fn Q3VButtonGroup::Q3VButtonGroup(QWidget *parent, const char *name)
+
+ Constructs a vertical button group with no title.
+
+ The \a parent and \a name arguments are passed on to the QWidget
+ constructor.
+*/
+
+/*!
+ \fn Q3VButtonGroup::Q3VButtonGroup(const QString &title, QWidget *parent,
+ const char *name)
+
+ Constructs a vertical button group with the title \a title.
+
+ The \a parent and \a name arguments are passed on to the QWidget
+ constructor.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3buttongroup.h b/src/qt3support/widgets/q3buttongroup.h
new file mode 100644
index 0000000..83ea594
--- /dev/null
+++ b/src/qt3support/widgets/q3buttongroup.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3BUTTONGROUP_H
+#define Q3BUTTONGROUP_H
+
+#include <QtGui/qbuttongroup.h>
+#include <Qt3Support/q3groupbox.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QAbstractButton;
+
+class Q_COMPAT_EXPORT Q3ButtonGroup : public Q3GroupBox
+{
+ Q_OBJECT
+ Q_PROPERTY(bool exclusive READ isExclusive WRITE setExclusive)
+ Q_PROPERTY(bool radioButtonExclusive READ isRadioButtonExclusive WRITE setRadioButtonExclusive)
+ Q_PROPERTY(int selectedId READ selectedId WRITE setButton)
+
+public:
+ Q3ButtonGroup(QWidget* parent=0, const char* name=0);
+ Q3ButtonGroup(const QString &title,
+ QWidget* parent=0, const char* name=0);
+ Q3ButtonGroup(int columns, Qt::Orientation o,
+ QWidget* parent=0, const char* name=0);
+ Q3ButtonGroup(int columns, Qt::Orientation o, const QString &title,
+ QWidget* parent=0, const char* name=0);
+ ~Q3ButtonGroup();
+
+ bool isExclusive() const;
+ bool isRadioButtonExclusive() const { return radio_excl; }
+ void setExclusive(bool);
+ void setRadioButtonExclusive(bool);
+
+public:
+ int insert(QAbstractButton *, int id=-1);
+ void remove(QAbstractButton *);
+ QAbstractButton *find(int id) const;
+ int id(QAbstractButton *) const;
+ int count() const;
+
+ void setButton(int id);
+
+ QAbstractButton *selected() const;
+ int selectedId() const;
+
+Q_SIGNALS:
+ void pressed(int id);
+ void released(int id);
+ void clicked(int id);
+
+protected Q_SLOTS:
+ void buttonPressed();
+ void buttonReleased();
+ void buttonClicked();
+
+protected:
+ bool event(QEvent * e);
+
+private Q_SLOTS:
+ void buttonDestroyed();
+
+private:
+ Q_DISABLE_COPY(Q3ButtonGroup)
+
+ void init();
+ void fixChildren() const;
+ int insert_helper(QAbstractButton* , int id=-1);
+ void remove_helper(QAbstractButton *);
+
+ bool excl_grp; // Not used.
+ bool radio_excl;
+ QMap<int, QAbstractButton*> buttonIds;
+ QButtonGroup group;
+};
+
+class Q_COMPAT_EXPORT Q3VButtonGroup : public Q3ButtonGroup
+{
+ Q_OBJECT
+public:
+ inline Q3VButtonGroup(QWidget* parent=0, const char* name=0)
+ : Q3ButtonGroup(1, Qt::Horizontal /* sic! */, parent, name) {}
+ inline Q3VButtonGroup(const QString &title, QWidget* parent=0, const char* name=0)
+ : Q3ButtonGroup(1, Qt::Horizontal /* sic! */, title, parent, name) {}
+
+private:
+ Q_DISABLE_COPY(Q3VButtonGroup)
+};
+
+
+class Q_COMPAT_EXPORT Q3HButtonGroup : public Q3ButtonGroup
+{
+ Q_OBJECT
+public:
+ inline Q3HButtonGroup(QWidget* parent=0, const char* name=0)
+ : Q3ButtonGroup(1, Qt::Vertical /* sic! */, parent, name) {}
+ inline Q3HButtonGroup(const QString &title, QWidget* parent=0, const char* name=0)
+ : Q3ButtonGroup(1, Qt::Vertical /* sic! */, title, parent, name) {}
+
+private:
+ Q_DISABLE_COPY(Q3HButtonGroup)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3BUTTONGROUP_H
diff --git a/src/qt3support/widgets/q3combobox.cpp b/src/qt3support/widgets/q3combobox.cpp
new file mode 100644
index 0000000..e8341ac
--- /dev/null
+++ b/src/qt3support/widgets/q3combobox.cpp
@@ -0,0 +1,2357 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3combobox.h"
+#ifndef QT_NO_COMBOBOX
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qpixmap.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "qlineedit.h"
+#include "qbitmap.h"
+#include "qstringlist.h"
+#include "qstyle.h"
+#include "qevent.h"
+#include "qmenu.h"
+#include "qmenudata.h"
+#include "qstyleoption.h"
+#include "qdesktopwidget.h"
+#include "q3popupmenu.h"
+#include "q3listbox.h"
+#include "q3strlist.h"
+#include "q3frame.h"
+#include <limits.h>
+#include <qdebug.h>
+#ifndef QT_NO_EFFECTS
+# include <private/qeffects_p.h>
+#endif
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+#include "qaccessible.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3ComboBox
+ \brief The Q3ComboBox widget is a combined button and popup list.
+ \since 4.1
+ \compat
+
+ A combobox is a selection widget which displays the current item
+ and can pop up a list of items. A combobox may be editable in
+ which case the user can enter arbitrary strings.
+
+ Comboboxes provide a means of showing the user's current choice
+ out of a list of options in a way that takes up the minimum amount
+ of screen space.
+
+ Q3ComboBox supports three different display styles: Aqua/Motif 1.x,
+ Motif 2.0 and Windows. In Motif 1.x, a combobox was called
+ XmOptionMenu. In Motif 2.0, OSF introduced an improved combobox
+ and named that XmComboBox. Q3ComboBox provides both.
+
+ Q3ComboBox provides two different constructors. The simplest
+ constructor creates an "old-style" combobox in Motif (or Aqua)
+ style:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3combobox.cpp 0
+
+ The other constructor creates a new-style combobox in Motif style,
+ and can create both read-only and editable comboboxes:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3combobox.cpp 1
+
+ New-style comboboxes use a list box in both Motif and Windows
+ styles, and both the content size and the on-screen size of the
+ list box can be limited with sizeLimit() and setMaxCount()
+ respectively. Old-style comboboxes use a popup in Aqua and Motif
+ style, and that popup will happily grow larger than the desktop if
+ you put enough data into it.
+
+ The two constructors create identical-looking comboboxes in
+ Windows style.
+
+ Comboboxes can contain pixmaps as well as strings; the
+ insertItem() and changeItem() functions are suitably overloaded.
+ For editable comboboxes, the function clearEdit() is provided,
+ to clear the displayed string without changing the combobox's
+ contents.
+
+ A combobox emits two signals, activated() and highlighted(), when
+ a new item has been activated (selected) or highlighted (made
+ current). Both signals exist in two versions, one with a \c
+ QString argument and one with an \c int argument. If the user
+ highlights or activates a pixmap, only the \c int signals are
+ emitted. Whenever the text of an editable combobox is changed the
+ textChanged() signal is emitted.
+
+ When the user enters a new string in an editable combobox, the
+ widget may or may not insert it, and it can insert it in several
+ locations. The default policy is is \c AtBottom but you can change
+ this using setInsertionPolicy().
+
+ It is possible to constrain the input to an editable combobox
+ using QValidator; see setValidator(). By default, any input is
+ accepted.
+
+ If the combobox is not editable then it has a default
+ focusPolicy() of \c TabFocus, i.e. it will not grab focus if
+ clicked. This differs from both Windows and Motif. If the combobox
+ is editable then it has a default focusPolicy() of \c StrongFocus,
+ i.e. it will grab focus if clicked.
+
+ A combobox can be populated using the insert functions,
+ insertStringList() and insertItem() for example. Items can be
+ changed with changeItem(). An item can be removed with
+ removeItem() and all items can be removed with clear(). The text
+ of the current item is returned by currentText(), and the text of
+ a numbered item is returned with text(). The current item can be
+ set with setCurrentItem() or setCurrentText(). The number of items
+ in the combobox is returned by count(); the maximum number of
+ items can be set with setMaxCount(). You can allow editing using
+ setEditable(). For editable comboboxes you can set auto-completion
+ using setAutoCompletion() and whether or not the user can add
+ duplicates is set with setDuplicatesEnabled().
+
+ Depending on the style, Q3ComboBox will use a list box or a
+ popup menu to display the list of items. See setListBox() for
+ more information.
+
+ \sa QComboBox, QLineEdit, QSpinBox
+ {GUI Design Handbook}{GUI Design Handbook: Combo Box, Drop-Down List Box}
+*/
+
+
+/*!
+ \enum Q3ComboBox::Policy
+
+ This enum specifies what the Q3ComboBox should do when a new string
+ is entered by the user.
+
+ \value NoInsertion the string will not be inserted into the
+ combobox.
+
+ \value AtTop insert the string as the first item in the combobox.
+
+ \value AtCurrent replace the previously selected item with the
+ string the user has entered.
+
+ \value AtBottom insert the string as the last item in the
+ combobox.
+
+ \value AfterCurrent insert the string after the previously
+ selected item.
+
+ \value BeforeCurrent insert the string before the previously
+ selected item.
+
+ activated() is always emitted when the string is entered.
+
+ If inserting the new string would cause the combobox to breach its
+ content size limit, the item at the other end of the list is
+ deleted. The definition of "other end" is
+ implementation-dependent.
+
+ \omitvalue NoInsert
+ \omitvalue InsertAtTop
+ \omitvalue InsertAtCurrent
+ \omitvalue InsertAtBottom
+ \omitvalue InsertAfterCurrent
+ \omitvalue InsertBeforeCurrent
+*/
+
+
+/*!
+ \fn void Q3ComboBox::activated( int index )
+
+ This signal is emitted when a new item has been activated
+ (selected). The \a index is the position of the item in the
+ combobox.
+
+ This signal is not emitted if the item is changed
+ programmatically, e.g. using setCurrentItem().
+*/
+
+/*!
+ \overload
+ \fn void Q3ComboBox::activated( const QString &string )
+
+ This signal is emitted when a new item has been activated
+ (selected). \a string is the selected string.
+
+ You can also use the activated(int) signal, but be aware that its
+ argument is meaningful only for selected strings, not for user
+ entered strings.
+*/
+
+/*!
+ \fn void Q3ComboBox::highlighted( int index )
+
+ This signal is emitted when a new item has been set to be the
+ current item. The \a index is the position of the item in the
+ combobox.
+
+ This signal is not emitted if the item is changed
+ programmatically, e.g. using setCurrentItem().
+*/
+
+/*!
+ \overload
+ \fn void Q3ComboBox::highlighted( const QString &string )
+
+ This signal is emitted when a new item has been set to be the
+ current item. \a string is the item's text.
+
+ You can also use the highlighted(int) signal.
+*/
+
+/*!
+ \fn void Q3ComboBox::textChanged( const QString &string )
+
+ This signal is used for editable comboboxes. It is emitted
+ whenever the contents of the text entry field changes. \a string
+ contains the new text.
+*/
+
+/*!
+ \property Q3ComboBox::autoCompletion
+ \brief whether auto-completion is enabled
+
+ This property can only be set for editable comboboxes, for
+ non-editable comboboxes it has no effect. It is false by default.
+*/
+
+/*!
+ \property Q3ComboBox::autoResize
+ \brief whether auto-resize is enabled
+ \obsolete
+
+ If this property is set to true then the combobox will resize
+ itself whenever its contents change. The default is false.
+*/
+
+/*!
+ \property Q3ComboBox::count
+ \brief the number of items in the combobox
+*/
+
+/*!
+ \property Q3ComboBox::currentItem
+ \brief the index of the current item in the combobox
+
+ Note that the activated() and highlighted() signals are only
+ emitted when the user changes the current item, not when it is
+ changed programmatically.
+*/
+
+/*!
+ \property Q3ComboBox::currentText
+ \brief the text of the combobox's current item
+*/
+
+/*!
+ \property Q3ComboBox::duplicatesEnabled
+ \brief whether duplicates are allowed
+
+ If the combobox is editable and the user enters some text in the
+ combobox's lineedit and presses Enter (and the insertionPolicy()
+ is not \c NoInsertion), then what happens is this:
+ \list
+ \i If the text is not already in the list, the text is inserted.
+ \i If the text is in the list and this property is true (the
+ default), the text is inserted.
+ \i If the text is in the list and this property is false, the text
+ is \e not inserted; instead the item which has matching text becomes
+ the current item.
+ \endlist
+
+ This property only affects user-interaction. You can use
+ insertItem() to insert duplicates if you wish regardless of this
+ setting.
+*/
+
+/*!
+ \property Q3ComboBox::editable
+ \brief whether the combobox is editable
+
+ This property's default is false. Note that the combobox will be
+ cleared if this property is set to true for a 1.x Motif style
+ combobox. To avoid this, use setEditable() before inserting any
+ items. Also note that the 1.x version of Motif didn't have any
+ editable comboboxes, so the combobox will change its appearance
+ to a 2.0 style Motif combobox is it is set to be editable.
+*/
+
+/*!
+ \property Q3ComboBox::insertionPolicy
+ \brief the position of the items inserted by the user
+
+ The default insertion policy is \c AtBottom. See \l Policy.
+*/
+
+/*!
+ \property Q3ComboBox::maxCount
+ \brief the maximum number of items allowed in the combobox
+*/
+
+/*!
+ \property Q3ComboBox::sizeLimit
+ \brief the maximum on-screen size of the combobox.
+
+ This property is ignored for both Motif 1.x style and non-editable
+ comboboxes in Mac style. The default limit is ten
+ lines. If the number of items in the combobox is or grows larger
+ than lines, a scroll bar is added.
+*/
+
+class Q3ComboBoxPopup : public Q3PopupMenu
+{
+public:
+ Q3ComboBoxPopup( QWidget *parent=0, const char *name=0 )
+ : Q3PopupMenu( parent, name )
+ {
+ }
+
+ int itemHeight( int index )
+ {
+ return Q3PopupMenu::itemHeight( index );
+ }
+};
+
+static inline QString escapedComboString(const QString &str)
+{
+ QString stringToReturn = str;
+ return stringToReturn.replace(QLatin1Char('&'), QLatin1String("&&"));
+}
+
+class Q3ComboBoxPopupItem : public QMenuItem
+{
+ Q3ListBoxItem *li;
+ QSize sc; // Size cache optimization
+public:
+ Q3ComboBoxPopupItem(Q3ListBoxItem *i) : QMenuItem(), li(i), sc(0, 0) { }
+ virtual bool fullSpan() const { return true; }
+ virtual void paint( QPainter*, const QColorGroup&, bool, bool, int, int, int, int);
+ virtual QSize sizeHint() { if (sc.isNull()) sc = QSize(li->width(li->listBox()), QMAX(25, li->height(li->listBox()))); return sc; }
+};
+void Q3ComboBoxPopupItem::paint( QPainter* p, const QColorGroup&, bool,
+ bool, int x, int y, int, int)
+{
+ p->save();
+ p->translate(x, y + ((sizeHint().height() / 2) - (li->height(li->listBox()) / 2)));
+ li->paint(p);
+ p->restore();
+}
+
+
+class Q3ComboBoxData
+{
+public:
+ Q3ComboBoxData( Q3ComboBox *cb ): current( 0 ), arrowDown(false), ed( 0 ), usingLBox( false ), pop( 0 ), lBox( 0 ), combo( cb )
+ {
+ duplicatesEnabled = true;
+ cb->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
+ }
+
+ inline bool usingListBox() { return usingLBox; }
+ inline Q3ListBox * listBox() { return lBox; }
+ inline Q3ComboBoxPopup * popup() { return pop; }
+ void updateLinedGeometry();
+
+ void setListBox( Q3ListBox *l ) { lBox = l ; usingLBox = true;
+ l->setMouseTracking( true );}
+
+ void setPopupMenu( Q3ComboBoxPopup * pm, bool isPopup=true )
+ { pop = pm; if(isPopup) usingLBox = false; }
+
+ QStyleOptionComboBox getStyleOption() const
+ {
+ QStyleOptionComboBox opt;
+ opt.init(combo);
+ if (!combo->editable() && combo->hasFocus())
+ opt.state |= QStyle::State_Selected;
+ opt.subControls = QStyle::SC_All;
+ if (arrowDown) {
+ opt.activeSubControls = QStyle::SC_ComboBoxArrow;
+ opt.state |= QStyle::State_Sunken;
+ }
+ opt.editable = combo->editable();
+ opt.frame = 1; // ### get from style?
+ if (current > -1 && current < combo->count()) {
+ opt.currentText = combo->text(current);
+ if (combo->pixmap(current))
+ opt.currentIcon = QIcon(*combo->pixmap(current));
+ }
+ opt.iconSize = QSize(22, 22); // ### need a sane value here
+// if (container && container->isVisible())
+// opt.state |= QStyle::State_On;
+ return opt;
+ }
+
+ int current;
+ int maxCount;
+ int sizeLimit;
+ Q3ComboBox::Policy p;
+ bool autoresize;
+ bool poppedUp;
+ bool mouseWasInsidePopup;
+ bool arrowPressed;
+ bool arrowDown;
+ bool discardNextMousePress;
+ bool shortClick;
+ bool useCompletion;
+ bool completeNow;
+ int completeAt;
+ bool duplicatesEnabled;
+ int fullHeight, currHeight;
+
+ QLineEdit * ed; // /bin/ed rules!
+ QTimer *completionTimer;
+
+ QSize sizeHint;
+ QHash<int, QPixmap> popupPixmaps;
+
+private:
+ bool usingLBox;
+ Q3ComboBoxPopup *pop;
+ Q3ListBox *lBox;
+ Q3ComboBox *combo;
+};
+
+void Q3ComboBoxData::updateLinedGeometry()
+{
+ if ( !ed || !combo )
+ return;
+
+ QStyleOptionComboBox opt = getStyleOption();
+ QRect r = combo->style()->subControlRect(
+ QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, combo);
+
+ const QPixmap *pix = current < combo->count() ? combo->pixmap( current ) : 0;
+ if ( pix && pix->width() < r.width() )
+ r.setLeft( r.left() + pix->width() + 4 );
+ if ( r != ed->geometry() )
+ ed->setGeometry( r );
+}
+
+static inline bool checkInsertIndex( const char *method, const char * name,
+ int count, int *index)
+{
+ bool range_err = (*index > count);
+#if defined(QT_CHECK_RANGE)
+ if ( range_err )
+ qWarning( "Q3ComboBox::%s: (%s) Index %d out of range",
+ method, name ? name : "<no name>", *index );
+#else
+ Q_UNUSED( method )
+ Q_UNUSED( name )
+#endif
+ if ( *index < 0 ) // append
+ *index = count;
+ return !range_err;
+}
+
+
+static inline bool checkIndex( const char *method, const char * name,
+ int count, int index )
+{
+ bool range_err = (index >= count);
+#if defined(QT_CHECK_RANGE)
+ if ( range_err )
+ qWarning( "Q3ComboBox::%s: (%s) Index %i out of range",
+ method, name ? name : "<no name>", index );
+#else
+ Q_UNUSED( method )
+ Q_UNUSED( name )
+#endif
+ return !range_err;
+}
+
+
+
+/*!
+ Constructs a combobox widget with parent \a parent called \a name.
+
+ This constructor creates a popup list if the program uses Motif
+ (or Aqua) look and feel; this is compatible with Motif 1.x and
+ Aqua.
+
+ Note: If you use this constructor to create your Q3ComboBox, then
+ the pixmap() function will always return 0. To workaround this,
+ use the other constructor.
+
+*/
+
+
+
+Q3ComboBox::Q3ComboBox( QWidget *parent, const char *name )
+ : QWidget( parent, name, Qt::WNoAutoErase )
+{
+ d = new Q3ComboBoxData( this );
+ QStyleOptionComboBox opt;
+ opt.init(this);
+ if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) ||
+ style()->styleHint(QStyle::SH_GUIStyle, &opt, this) == Qt::MotifStyle ) {
+ d->setPopupMenu( new Q3ComboBoxPopup( this, "in-combo" ) );
+ d->popup()->setFont( font() );
+ connect( d->popup(), SIGNAL(activated(int)),
+ SLOT(internalActivate(int)) );
+ connect( d->popup(), SIGNAL(highlighted(int)),
+ SLOT(internalHighlight(int)) );
+ } else {
+ setUpListBox();
+ }
+ d->ed = 0;
+ d->current = 0;
+ d->maxCount = INT_MAX;
+ d->sizeLimit = 10;
+ d->p = AtBottom;
+ d->autoresize = false;
+ d->poppedUp = false;
+ d->arrowDown = false;
+ d->arrowPressed = false;
+ d->discardNextMousePress = false;
+ d->shortClick = false;
+ d->useCompletion = false;
+ d->completeAt = 0;
+ d->completeNow = false;
+ d->completionTimer = new QTimer( this );
+
+ setFocusPolicy( Qt::TabFocus );
+ setBackgroundMode( Qt::PaletteButton );
+}
+
+
+/*!
+ Constructs a combobox with a maximum size and either Motif 2.0 or
+ Windows look and feel.
+
+ The input field can be edited if \a rw is true, otherwise the user
+ may only choose one of the items in the combobox.
+
+ The \a parent and \a name arguments are passed on to the QWidget
+ constructor.
+*/
+
+
+Q3ComboBox::Q3ComboBox( bool rw, QWidget *parent, const char *name )
+ : QWidget( parent, name, Qt::WNoAutoErase )
+{
+ d = new Q3ComboBoxData( this );
+ setUpListBox();
+
+ QStyleOptionComboBox opt = d->getStyleOption();
+ if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
+ d->popup()->setItemChecked(d->current, false);
+ d->maxCount = INT_MAX;
+ setSizeLimit(10);
+ d->p = AtBottom;
+ d->autoresize = false;
+ d->poppedUp = false;
+ d->arrowDown = false;
+ d->discardNextMousePress = false;
+ d->shortClick = false;
+ d->useCompletion = false;
+ d->completeAt = 0;
+ d->completeNow = false;
+ d->completionTimer = new QTimer( this );
+
+ setFocusPolicy( Qt::StrongFocus );
+
+ d->ed = 0;
+ if ( rw )
+ setUpLineEdit();
+ setBackgroundMode( Qt::PaletteButton, Qt::PaletteBase );
+}
+
+
+
+/*!
+ Destroys the combobox.
+*/
+
+Q3ComboBox::~Q3ComboBox()
+{
+ delete d;
+}
+
+void Q3ComboBox::setDuplicatesEnabled( bool enable )
+{
+ d->duplicatesEnabled = enable;
+}
+
+bool Q3ComboBox::duplicatesEnabled() const
+{
+ return d->duplicatesEnabled;
+}
+
+int Q3ComboBox::count() const
+{
+ if ( d->usingListBox() )
+ return d->listBox()->count();
+ else if (d->popup())
+ return d->popup()->count();
+ else
+ return 0;
+}
+
+
+/*!
+ \overload
+
+ Inserts the \a list of strings at position \a index in the
+ combobox.
+
+ This is only for compatibility since it does not support Unicode
+ strings. See insertStringList().
+*/
+
+void Q3ComboBox::insertStrList( const Q3StrList &list, int index )
+{
+ insertStrList( &list, index );
+}
+
+/*!
+ \overload
+
+ Inserts the \a list of strings at position \a index in the
+ combobox.
+
+ This is only for compatibility since it does not support Unicode
+ strings. See insertStringList().
+*/
+
+void Q3ComboBox::insertStrList( const Q3StrList *list, int index )
+{
+ if ( !list ) {
+#if defined(QT_CHECK_NULL)
+ Q_ASSERT( list != 0 );
+#endif
+ return;
+ }
+ Q3StrListIterator it( *list );
+ const char* tmp;
+ if ( index < 0 )
+ index = count();
+ while ( (tmp=it.current()) ) {
+ ++it;
+ if ( d->usingListBox() )
+ d->listBox()->insertItem( QString::fromLatin1(tmp), index );
+ else
+ d->popup()->insertItem( escapedComboString(QString::fromLatin1(tmp)), index, index );
+ if ( index++ == d->current && d->current < count() ) {
+ if ( d->ed ) {
+ d->ed->setText( text( d->current ) );
+ d->updateLinedGeometry();
+ } else
+ update();
+ currentChanged();
+ }
+ }
+ if ( index != count() )
+ reIndex();
+}
+
+/*!
+ Inserts the \a list of strings at position \a index in the
+ combobox.
+*/
+
+void Q3ComboBox::insertStringList( const QStringList &list, int index )
+{
+ QStringList::ConstIterator it = list.begin();
+ if ( index < 0 )
+ index = count();
+ while ( it != list.end() ) {
+ if ( d->usingListBox() )
+ d->listBox()->insertItem( *it, index );
+ else
+ d->popup()->insertItem( escapedComboString(*it), index, index );
+ if ( index++ == d->current && d->current < count() ) {
+ if ( d->ed ) {
+ d->ed->setText( text( d->current ) );
+ d->updateLinedGeometry();
+ } else
+ update();
+ currentChanged();
+ }
+ ++it;
+ }
+ if ( index != count() )
+ reIndex();
+}
+
+/*!
+ Inserts the array of char * \a strings at position \a index in the
+ combobox.
+
+ The \a numStrings argument is the number of strings. If \a
+ numStrings is -1 (default), the \a strings array must be
+ terminated with 0.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3combobox.cpp 2
+
+ \sa insertStringList()
+*/
+
+void Q3ComboBox::insertStrList( const char **strings, int numStrings, int index)
+{
+ if ( !strings ) {
+#if defined(QT_CHECK_NULL)
+ Q_ASSERT( strings != 0 );
+#endif
+ return;
+ }
+ if ( index < 0 )
+ index = count();
+ int i = 0;
+ while ( (numStrings<0 && strings[i]!=0) || i<numStrings ) {
+ if ( d->usingListBox() )
+ d->listBox()->insertItem( QString::fromLatin1(strings[i]), index );
+ else
+ d->popup()->insertItem( escapedComboString(QString::fromLatin1(strings[i])), index, index );
+ i++;
+ if ( index++ == d->current && d->current < count() ) {
+ if ( d->ed ) {
+ d->ed->setText( text( d->current ) );
+ d->updateLinedGeometry();
+ } else
+ update();
+ currentChanged();
+ }
+ }
+ if ( index != count() )
+ reIndex();
+}
+
+
+/*!
+ Inserts a text item with text \a t, at position \a index. The item
+ will be appended if \a index is negative.
+*/
+
+void Q3ComboBox::insertItem( const QString &t, int index )
+{
+ int cnt = count();
+ if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) )
+ return;
+ if ( d->usingListBox() )
+ d->listBox()->insertItem( t, index );
+ else
+ d->popup()->insertItem( escapedComboString(t), index, index );
+ if ( index != cnt )
+ reIndex();
+ if ( index == d->current && d->current < count() ) {
+ if ( d->ed ) {
+ d->ed->setText( text( d->current ) );
+ d->updateLinedGeometry();
+ } else
+ update();
+ }
+ if ( index == d->current )
+ currentChanged();
+}
+
+/*!
+ \overload
+
+ Inserts a \a pixmap item at position \a index. The item will be
+ appended if \a index is negative.
+*/
+
+void Q3ComboBox::insertItem( const QPixmap &pixmap, int index )
+{
+ int cnt = count();
+ if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) )
+ return;
+ if ( d->usingListBox() )
+ d->listBox()->insertItem( pixmap, index );
+ else
+ d->popup()->insertItem( pixmap, index, index );
+ if ( index != cnt )
+ reIndex();
+ if ( index == d->current && d->current < count() ) {
+ if ( d->ed ) {
+ d->ed->setText( text( d->current ) );
+ d->updateLinedGeometry();
+ } else
+ update();
+ }
+ if ( index == d->current )
+ currentChanged();
+}
+
+/*!
+ \overload
+
+ Inserts a \a pixmap item with additional text \a text at position
+ \a index. The item will be appended if \a index is negative.
+*/
+
+void Q3ComboBox::insertItem( const QPixmap &pixmap, const QString& text, int index )
+{
+ int cnt = count();
+ if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) )
+ return;
+ if ( d->usingListBox() )
+ d->listBox()->insertItem( pixmap, text, index );
+ else
+ d->popup()->insertItem( pixmap, escapedComboString(text), index, index );
+ if ( index != cnt )
+ reIndex();
+ if ( index == d->current && d->current < count() ) {
+ if ( d->ed ) {
+ d->ed->setText( this->text( d->current ) );
+ d->updateLinedGeometry();
+ } else
+ update();
+ }
+ if ( index == d->current )
+ currentChanged();
+}
+
+
+/*!
+ Removes the item at position \a index.
+*/
+
+void Q3ComboBox::removeItem( int index )
+{
+ int cnt = count();
+ if ( !checkIndex( "removeItem", name(), cnt, index ) )
+ return;
+ if ( d->usingListBox() ) {
+ QStyleOptionComboBox opt = d->getStyleOption();
+ if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && d->popup() )
+ d->popup()->removeItemAt( index );
+ d->listBox()->removeItem( index );
+ } else {
+ d->popup()->removeItemAt( index );
+ }
+ if ( index != cnt-1 )
+ reIndex();
+ if ( index == d->current ) {
+ if ( d->ed ) {
+ QString s = QString::fromLatin1("");
+ if (d->current < cnt - 1)
+ s = text( d->current );
+ d->ed->setText( s );
+ d->updateLinedGeometry();
+ }
+ else {
+ if ( d->usingListBox() ) {
+ d->current = d->listBox()->currentItem();
+ } else {
+ if (d->current > count()-1 && d->current > 0)
+ d->current--;
+ }
+ update();
+ }
+ currentChanged();
+ }
+ else {
+ if ( !d->ed ) {
+ if (d->current < cnt - 1)
+ setCurrentItem( d->current );
+ else
+ setCurrentItem( d->current - 1 );
+ }
+ }
+
+}
+
+
+/*!
+ Removes all combobox items.
+*/
+
+void Q3ComboBox::clear()
+{
+ QStyleOptionComboBox opt = d->getStyleOption();
+ if ( d->usingListBox() ) {
+ if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && d->popup() )
+ d->popup()->clear();
+ d->listBox()->resize( 0, 0 );
+ d->listBox()->clear();
+ } else {
+ d->popup()->clear();
+ }
+
+ if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
+ d->popup()->setItemChecked(d->current, false);
+ d->current = 0;
+ if ( d->ed ) {
+ d->ed->setText( QString::fromLatin1("") );
+ d->updateLinedGeometry();
+ }
+ currentChanged();
+}
+
+
+QString Q3ComboBox::currentText() const
+{
+ if ( d->ed )
+ return d->ed->text();
+ else if ( d->current < count() )
+ return text( currentItem() );
+ else
+ return QString::null;
+}
+
+void Q3ComboBox::setCurrentText( const QString& txt )
+{
+ int i;
+ for ( i = 0; i < count(); i++)
+ if ( text( i ) == txt )
+ break;
+ if ( i < count() )
+ setCurrentItem( i );
+ else if ( d->ed )
+ d->ed->setText( txt );
+ else
+ changeItem( txt, currentItem() );
+}
+
+
+/*!
+ Returns the text item at position \a index, or QString::null if
+ the item is not a string.
+
+ \sa currentText()
+*/
+
+QString Q3ComboBox::text( int index ) const
+{
+ if ( !checkIndex( "text", name(), count(), index ) )
+ return QString::null;
+ if ( d->usingListBox() ) {
+ return d->listBox()->text( index );
+ } else {
+ QString retText = d->popup()->text(index);
+ retText.replace(QLatin1String("&&"), QString(QLatin1Char('&')));
+ return retText;
+ }
+}
+
+/*!
+ Returns the pixmap item at position \a index, or 0 if the item is
+ not a pixmap.
+*/
+
+const QPixmap *Q3ComboBox::pixmap( int index ) const
+{
+ if ( !checkIndex( "pixmap", name(), count(), index ) )
+ return 0;
+
+ if (d->usingListBox()) {
+ return d->listBox()->pixmap( index );
+ } else {
+ d->popupPixmaps[index] = d->popup()->pixmap(index);
+ return d->popupPixmaps[index].isNull() ? 0 : &d->popupPixmaps[index];
+ }
+}
+
+/*!
+ Replaces the item at position \a index with the text \a t.
+*/
+
+void Q3ComboBox::changeItem( const QString &t, int index )
+{
+ if ( !checkIndex( "changeItem", name(), count(), index ) )
+ return;
+ if ( d->usingListBox() )
+ d->listBox()->changeItem( t, index );
+ else
+ d->popup()->changeItem(index, t);
+ if ( index == d->current ) {
+ if ( d->ed ) {
+ d->ed->setText( text( d->current ) );
+ d->updateLinedGeometry();
+ } else
+ update();
+ }
+}
+
+/*!
+ \overload
+
+ Replaces the item at position \a index with the pixmap \a im,
+ unless the combobox is editable.
+
+ \sa insertItem()
+*/
+
+void Q3ComboBox::changeItem( const QPixmap &im, int index )
+{
+ if ( !checkIndex( "changeItem", name(), count(), index ) )
+ return;
+ if ( d->usingListBox() )
+ d->listBox()->changeItem( im, index );
+ else
+ d->popup()->changeItem(index, im);
+ if ( index == d->current )
+ update();
+}
+
+/*!
+ \overload
+
+ Replaces the item at position \a index with the pixmap \a im and
+ the text \a t.
+
+ \sa insertItem()
+*/
+
+void Q3ComboBox::changeItem( const QPixmap &im, const QString &t, int index )
+{
+ if ( !checkIndex( "changeItem", name(), count(), index ) )
+ return;
+ if ( d->usingListBox() )
+ d->listBox()->changeItem( im, t, index );
+ else
+ d->popup()->changeItem(index, im, t);
+ if ( index == d->current )
+ update();
+}
+
+
+int Q3ComboBox::currentItem() const
+{
+ return d->current;
+}
+
+void Q3ComboBox::setCurrentItem( int index )
+{
+ if ( index == d->current && !d->ed ) {
+ return;
+ }
+ if ( !checkIndex( "setCurrentItem", name(), count(), index ) ) {
+ return;
+ }
+
+ if ( d->usingListBox() && !( listBox()->item(index) && listBox()->item(index)->isSelectable() ) )
+ return;
+
+ QStyleOptionComboBox opt = d->getStyleOption();
+ if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
+ d->popup()->setItemChecked(d->current, false);
+ d->current = index;
+ d->completeAt = 0;
+ if ( d->ed ) {
+ d->ed->setText( text( index ) );
+ d->updateLinedGeometry();
+ }
+ // ### We want to keep ListBox's currentItem in sync, even if NOT popuped...
+ if ( d->usingListBox() && d->listBox() ) {
+ d->listBox()->setCurrentItem( index );
+ } else {
+ internalHighlight( index );
+ // internalActivate( index ); ### this leads to weird behavior, as in 3.0.1
+ }
+
+ currentChanged();
+}
+
+/*!
+ Returns true if auto-resize is enabled; otherwise returns false.
+
+ \sa autoResize
+*/
+
+bool Q3ComboBox::autoResize() const
+{
+ return d->autoresize;
+}
+
+/*!
+ If \a enable is true, enable auto-resize; disable it otherwise.
+
+ \sa autoResize
+*/
+
+void Q3ComboBox::setAutoResize( bool enable )
+{
+ if ( (bool)d->autoresize != enable ) {
+ d->autoresize = enable;
+ if ( enable )
+ adjustSize();
+ }
+}
+
+
+/*!
+ \reimp
+
+ This implementation caches the size hint to avoid resizing when
+ the contents change dynamically. To invalidate the cached value
+ call setFont().
+*/
+QSize Q3ComboBox::sizeHint() const
+{
+ if ( isVisible() && d->sizeHint.isValid() )
+ return d->sizeHint;
+
+ constPolish();
+ int i, w;
+ QFontMetrics fm = fontMetrics();
+
+ int maxW = count() ? 18 : 7 * fm.width(QLatin1Char('x')) + 18;
+ int maxH = QMAX( fm.lineSpacing(), 14 ) + 2;
+
+ if ( !d->usingListBox() ) {
+ w = d->popup()->sizeHint().width() - 2* d->popup()->frameWidth();
+ if ( w > maxW )
+ maxW = w;
+ } else {
+ for( i = 0; i < count(); i++ ) {
+ w = d->listBox()->item( i )->width( d->listBox() );
+ if ( w > maxW )
+ maxW = w;
+ }
+ }
+
+ QStyleOptionComboBox opt = d->getStyleOption();
+ d->sizeHint = (style()->sizeFromContents(QStyle::CT_ComboBox, &opt, QSize(maxW, maxH), this).
+ expandedTo(QApplication::globalStrut()));
+
+ return d->sizeHint;
+}
+
+
+/*!
+ \internal
+ Receives activated signals from an internal popup list and emits
+ the activated() signal.
+*/
+
+void Q3ComboBox::internalActivate( int index )
+{
+ QStyleOptionComboBox opt = d->getStyleOption();
+ if ( d->current != index ) {
+ if ( !d->usingListBox() || listBox()->item( index )->isSelectable() ) {
+ if (d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
+ d->popup()->setItemChecked(d->current, false);
+ d->current = index;
+ currentChanged();
+ }
+ }
+ if ( d->usingListBox() )
+ popDownListBox();
+ else
+ d->popup()->removeEventFilter( this );
+ d->poppedUp = false;
+
+ QString t( text( index ) );
+ if ( d->ed ) {
+ d->ed->setText( t );
+ d->updateLinedGeometry();
+ }
+ emit activated( index );
+ emit activated( t );
+}
+
+/*!
+ \internal
+ Receives highlighted signals from an internal popup list and emits
+ the highlighted() signal.
+*/
+
+void Q3ComboBox::internalHighlight( int index )
+{
+ emit highlighted( index );
+ QString t = text( index );
+ if ( !t.isNull() )
+ emit highlighted( t );
+}
+
+/*!
+ \internal
+ Receives timeouts after a click. Used to decide if a Motif style
+ popup should stay up or not after a click.
+*/
+void Q3ComboBox::internalClickTimeout()
+{
+ d->shortClick = false;
+}
+
+/*!
+ Sets the palette for both the combobox button and the combobox
+ popup list to \a palette.
+*/
+
+void Q3ComboBox::setPalette( const QPalette &palette )
+{
+ QWidget::setPalette( palette );
+ if ( d->listBox() )
+ d->listBox()->setPalette( palette );
+ if ( d->popup() )
+ d->popup()->setPalette( palette );
+}
+
+/*!
+ Sets the font for both the combobox button and the combobox popup
+ list to \a font.
+*/
+
+void Q3ComboBox::setFont( const QFont &font )
+{
+ d->sizeHint = QSize(); // invalidate size hint
+ QWidget::setFont( font );
+ if ( d->usingListBox() )
+ d->listBox()->setFont( font );
+ else
+ d->popup()->setFont( font );
+ if (d->ed)
+ d->ed->setFont( font );
+ if ( d->autoresize )
+ adjustSize();
+}
+
+
+/*!\reimp
+*/
+
+void Q3ComboBox::resizeEvent( QResizeEvent * e )
+{
+ if ( d->ed )
+ d->updateLinedGeometry();
+ if ( d->listBox() )
+ d->listBox()->resize( width(), d->listBox()->height() );
+ QWidget::resizeEvent( e );
+}
+
+/*!\reimp
+*/
+
+void Q3ComboBox::paintEvent( QPaintEvent * )
+{
+ QPainter p( this );
+ const QColorGroup & g = colorGroup();
+ p.setPen(g.text());
+
+ if ( width() < 5 || height() < 5 ) {
+ qDrawShadePanel( &p, rect(), g, false, 2,
+ &g.brush( QColorGroup::Button ) );
+ return;
+ }
+
+ QStyleOptionComboBox opt = d->getStyleOption();
+ bool reverse = QApplication::reverseLayout();
+ if ( !d->usingListBox() &&
+ style()->styleHint(QStyle::SH_GUIStyle) == Qt::MotifStyle) { // motif 1.x style
+ int dist, buttonH, buttonW;
+ dist = 8;
+ buttonH = 7;
+ buttonW = 11;
+ int xPos;
+ int x0;
+ int w = width() - dist - buttonW - 1;
+ if ( reverse ) {
+ xPos = dist + 1;
+ x0 = xPos + 4;
+ } else {
+ xPos = w;
+ x0 = 4;
+ }
+ qDrawShadePanel( &p, rect(), g, false,
+ style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this),
+ &g.brush( QColorGroup::Button ) );
+ qDrawShadePanel( &p, xPos, (height() - buttonH)/2,
+ buttonW, buttonH, g, false,
+ style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this) );
+ QRect clip( x0, 2, w - 2 - 4 - 5, height() - 4 );
+ QString str = d->popup()->text( this->d->current );
+ if ( !str.isNull() ) {
+ p.drawText( clip, Qt::AlignCenter | Qt::TextSingleLine, str );
+ }
+
+ QPixmap pix = d->popup()->pixmap( this->d->current );
+ QIcon iconSet = d->popup()->iconSet( this->d->current );
+ if (!pix.isNull() || !iconSet.isNull()) {
+ QPixmap pm = ( !pix.isNull() ? pix : iconSet.pixmap() );
+ p.setClipRect( clip );
+ p.drawPixmap( 4, (height()-pm.height())/2, pm );
+ p.setClipping( false );
+ }
+
+ if ( hasFocus() )
+ p.drawRect( xPos - 5, 4, width() - xPos + 1 , height() - 8 );
+ } else if(!d->usingListBox()) {
+ style()->drawComplexControl(QStyle::CC_ComboBox, &opt, &p, this);
+ QRect re = style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxEditField, this);
+ p.setClipRect( re );
+
+ QString str = d->popup()->text( this->d->current );
+ QPixmap pix = d->popup()->pixmap( this->d->current );
+ if ( !str.isNull() ) {
+ p.save();
+ p.setFont(font());
+ QFontMetrics fm(font());
+ int x = re.x(), y = re.y() + fm.ascent();
+ x += pix.width() + 5;
+ p.drawText( x, y, str );
+ p.restore();
+ }
+ if (!pix.isNull()) {
+ p.fillRect(re.x(), re.y(), pix.width() + 4, re.height(),
+ colorGroup().brush(QColorGroup::Base));
+ p.drawPixmap(re.x() + 2, re.y() + (re.height() - pix.height()) / 2, pix);
+ }
+ } else {
+ style()->drawComplexControl(QStyle::CC_ComboBox, &opt, &p, this);
+ QRect re = style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxEditField, this);
+ p.setClipRect(re);
+
+ if ( !d->ed ) {
+ Q3ListBoxItem * item = d->listBox()->item( d->current );
+ if ( item ) {
+ int itemh = item->height( d->listBox() );
+ p.translate( re.x(), re.y() + (re.height() - itemh)/2 );
+ item->paint( &p );
+ }
+ } else if ( d->listBox() && d->listBox()->item( d->current ) ) {
+ Q3ListBoxItem * item = d->listBox()->item( d->current );
+ const QPixmap *pix = item->pixmap();
+ if ( pix ) {
+ p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(),
+ colorGroup().brush( QColorGroup::Base ) );
+ p.drawPixmap( re.x() + 2, re.y() +
+ ( re.height() - pix->height() ) / 2, *pix );
+ }
+ }
+ p.setClipping( false );
+ }
+}
+
+
+/*!\reimp
+*/
+
+void Q3ComboBox::mousePressEvent( QMouseEvent *e )
+{
+ if ( e->button() != Qt::LeftButton )
+ return;
+ if ( d->discardNextMousePress ) {
+ d->discardNextMousePress = false;
+ return;
+ }
+
+ QStyleOptionComboBox opt = d->getStyleOption();
+ QRect arrowRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow
+ , this);
+
+ // Correction for motif style, where arrow is smaller
+ // and thus has a rect that doesn't fit the button.
+ arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) );
+
+ if ( count() && ( !editable() || arrowRect.contains( e->pos() ) ) ) {
+ d->arrowPressed = false;
+
+ if ( d->usingListBox() ) {
+ listBox()->blockSignals( true );
+ qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll
+ listBox()->setCurrentItem(d->current);
+ listBox()->blockSignals( false );
+ popup();
+ if ( arrowRect.contains( e->pos() ) ) {
+ d->arrowPressed = true;
+ d->arrowDown = true;
+ repaint( false );
+ }
+ } else {
+ popup();
+ }
+ QTimer::singleShot( 200, this, SLOT(internalClickTimeout()));
+ d->shortClick = true;
+ }
+}
+
+/*!\reimp
+*/
+
+void Q3ComboBox::mouseMoveEvent( QMouseEvent * )
+{
+}
+
+/*!\reimp
+*/
+
+void Q3ComboBox::mouseReleaseEvent( QMouseEvent * )
+{
+}
+
+/*!\reimp
+*/
+
+void Q3ComboBox::mouseDoubleClickEvent( QMouseEvent *e )
+{
+ mousePressEvent( e );
+}
+
+
+/*!\reimp
+*/
+
+void Q3ComboBox::keyPressEvent( QKeyEvent *e )
+{
+ bool handleEventHere = d->usingListBox() || !d->poppedUp;
+
+ int c = currentItem();
+ if ( ( e->key() == Qt::Key_F4 && e->state() == 0 ) ||
+ ( e->key() == Qt::Key_Down && (e->state() & Qt::AltModifier) ) ||
+ ( !d->ed && e->key() == Qt::Key_Space ) ) {
+ if ( count() ) {
+ if ( !d->usingListBox() )
+ d->popup()->setActiveItem( this->d->current );
+ popup();
+ }
+ return;
+ } else if ( handleEventHere && e->key() == Qt::Key_Up ) {
+ if ( c > 0 )
+ setCurrentItem( c-1 );
+ } else if ( handleEventHere && e->key() == Qt::Key_Down ) {
+ if ( ++c < count() )
+ setCurrentItem( c );
+ } else if ( handleEventHere && e->key() == Qt::Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) {
+ setCurrentItem( 0 );
+ } else if ( handleEventHere && e->key() == Qt::Key_End && ( !d->ed || !d->ed->hasFocus() ) ) {
+ setCurrentItem( count()-1 );
+ } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) {
+ if ( !d->completionTimer->isActive() ) {
+ d->completeAt = 0;
+ c = completionIndex( e->text(), ++c );
+ if ( c >= 0 ) {
+ setCurrentItem( c );
+ d->completeAt = e->text().length();
+ }
+ } else {
+ d->completionTimer->stop();
+ QString ct = currentText().left( d->completeAt ) + e->text();
+ c = completionIndex( ct, c );
+ if ( c < 0 && d->completeAt > 0 ) {
+ c = completionIndex( e->text(), 0 );
+ ct = e->text();
+ }
+ d->completeAt = 0;
+ if ( c >= 0 ) {
+ setCurrentItem( c );
+ d->completeAt = ct.length();
+ }
+ }
+ d->completionTimer->start( 400, true );
+ } else {
+ e->ignore();
+ return;
+ }
+
+ c = currentItem();
+ if ( count() && !text( c ).isNull() )
+ emit activated( text( c ) );
+ emit activated( c );
+}
+
+
+/*!\reimp
+*/
+
+void Q3ComboBox::focusInEvent( QFocusEvent * e )
+{
+ QWidget::focusInEvent( e );
+ d->completeNow = false;
+ d->completeAt = 0;
+}
+
+/*!\reimp
+*/
+
+void Q3ComboBox::focusOutEvent( QFocusEvent * e )
+{
+ QWidget::focusOutEvent( e );
+ d->completeNow = false;
+ d->completeAt = 0;
+}
+
+/*!\reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void Q3ComboBox::wheelEvent( QWheelEvent *e )
+{
+ if ( d->poppedUp ) {
+ if ( d->usingListBox() ) {
+ QApplication::sendEvent( d->listBox(), e );
+ }
+ } else {
+ if ( e->delta() > 0 ) {
+ int c = currentItem();
+ if ( c > 0 ) {
+ setCurrentItem( c-1 );
+ emit activated( currentItem() );
+ emit activated( currentText() );
+ }
+ } else {
+ int c = currentItem();
+ if ( ++c < count() ) {
+ setCurrentItem( c );
+ emit activated( currentItem() );
+ emit activated( currentText() );
+ }
+ }
+ e->accept();
+ }
+}
+#endif
+
+/*!
+ \internal
+ Calculates the listbox height needed to contain all items, or as
+ many as the list box is supposed to contain.
+*/
+static int listHeight( Q3ListBox *l, int sl )
+{
+ if ( l->count() > 0 )
+ return QMIN( l->count(), (uint)sl) * l->item( 0 )->height(l);
+ else
+ return l->sizeHint().height();
+}
+
+
+/*!
+ Pops up the combobox popup list.
+
+ If the list is empty, no items appear.
+*/
+
+void Q3ComboBox::popup()
+{
+ if ( !count() || d->poppedUp )
+ return;
+
+ QStyleOptionComboBox opt = d->getStyleOption();
+ if( !d->usingListBox() || style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) ) {
+ if(d->usingListBox()) {
+ if(!d->popup()) {
+ Q3ComboBoxPopup *p = new Q3ComboBoxPopup( this, "in-combo" );
+ d->setPopupMenu( p, false );
+ p->setFont( font() );
+ connect( p, SIGNAL(activated(int)), SLOT(internalActivate(int)) );
+ connect( p, SIGNAL(highlighted(int)), SLOT(internalHighlight(int)) );
+ }
+ d->popup()->clear();
+ for(unsigned int i = 0; i < d->listBox()->count(); i++) {
+ Q3ListBoxItem *item = d->listBox()->item(i);
+ if(item->rtti() == Q3ListBoxText::RTTI) {
+ d->popup()->insertItem(escapedComboString(item->text()), i, i);
+ } else if(item->rtti() == Q3ListBoxPixmap::RTTI) {
+ if(item->pixmap())
+ d->popup()->insertItem(QIcon(*item->pixmap()), escapedComboString(item->text()), i, i);
+ else
+ d->popup()->insertItem(escapedComboString(item->text()), i, i);
+ } else {
+ d->popup()->insertItem(new Q3ComboBoxPopupItem(item), i, i);
+ }
+ }
+ }
+ d->popup()->installEventFilter( this );
+ if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this))
+ d->popup()->setItemChecked(this->d->current, true);
+ d->popup()->popup( mapToGlobal( QPoint(0,0) ), this->d->current );
+ update();
+ } else {
+ // Send all listbox events to eventFilter():
+ Q3ListBox* lb = d->listBox();
+ lb->triggerUpdate( true );
+ lb->installEventFilter( this );
+ d->mouseWasInsidePopup = false;
+ int w = lb->variableWidth() ? lb->sizeHint().width() : width();
+ int h = listHeight( lb, d->sizeLimit ) + 2;
+ QRect screen = QApplication::desktop()->availableGeometry( this );
+
+ int sx = screen.x(); // screen pos
+ int sy = screen.y();
+ int sw = screen.width(); // screen width
+ int sh = screen.height(); // screen height
+ QPoint pos = mapToGlobal( QPoint(0,height()) );
+ // ## Similar code is in QPopupMenu
+ int x = pos.x();
+ int y = pos.y();
+
+ // the complete widget must be visible
+ if ( x + w > sx + sw )
+ x = sx+sw - w;
+ if ( x < sx )
+ x = sx;
+ if (y + h > sy+sh && y - h - height() >= 0 )
+ y = y - h - height();
+
+ opt.rect = QRect(x, y, w, h);
+ QRect rect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxListBoxPopup, this);
+
+ // work around older styles that don't implement the combobox
+ // listbox popup subcontrol
+ if ( rect.isNull() )
+ rect.setRect( x, y, w, h );
+ lb->setGeometry( rect );
+
+ lb->raise();
+ bool block = lb->signalsBlocked();
+ lb->blockSignals( true );
+ Q3ListBoxItem* currentLBItem = 0;
+ if ( editable() && currentText() != text( currentItem() ) )
+ currentLBItem = lb->findItem( currentText() );
+
+ currentLBItem = currentLBItem ? currentLBItem : lb->item( d->current );
+
+ lb->setCurrentItem( currentLBItem );
+ lb->setContentsPos( lb->contentsX(),
+ lb->viewportToContents( lb->itemRect( currentLBItem ).topLeft() ).y() );
+
+ // set the current item to also be the selected item if it isn't already
+ if ( currentLBItem && currentLBItem->isSelectable() && !currentLBItem->isSelected() )
+ lb->setSelected( currentLBItem, true );
+ lb->blockSignals( block );
+ lb->setVScrollBarMode(Q3ScrollView::Auto);
+
+#ifndef QT_NO_EFFECTS
+ if ( QApplication::isEffectEnabled( Qt::UI_AnimateCombo ) ) {
+ if ( lb->y() < mapToGlobal(QPoint(0,0)).y() )
+ qScrollEffect( lb, QEffects::UpScroll );
+ else
+ qScrollEffect( lb );
+ } else
+#endif
+ lb->show();
+ }
+ d->poppedUp = true;
+}
+
+
+/*!
+ Updates the widget mask.
+
+ \sa QWidget::setMask()
+*/
+void Q3ComboBox::updateMask()
+{
+ QBitmap bm( size() );
+ bm.fill( Qt::color0 );
+
+ QStyleOptionComboBox opt = d->getStyleOption();
+ {
+ QPainter p(&bm);
+ p.initFrom(this);
+ p.fillRect(opt.rect, Qt::color1); // qcommonstyle old drawComplexControl implementation
+ }
+
+ setMask( bm );
+}
+
+/*!
+ \internal
+ Pops down (removes) the combobox popup list box.
+*/
+void Q3ComboBox::popDownListBox()
+{
+ Q_ASSERT( d->usingListBox() );
+ d->listBox()->removeEventFilter( this );
+ d->listBox()->viewport()->removeEventFilter( this );
+ d->listBox()->hide();
+ d->listBox()->setCurrentItem( d->current );
+ if ( d->arrowDown ) {
+ d->arrowDown = false;
+ repaint( false );
+ }
+ d->poppedUp = false;
+}
+
+
+/*!
+ \internal
+ Re-indexes the identifiers in the popup list.
+*/
+
+void Q3ComboBox::reIndex()
+{
+ if ( !d->usingListBox() ) {
+ int cnt = count();
+ while ( cnt-- )
+ d->popup()->setId( cnt, cnt );
+ }
+}
+
+/*!
+ \internal
+ Repaints the combobox.
+*/
+
+void Q3ComboBox::currentChanged()
+{
+ if ( d->autoresize )
+ adjustSize();
+ update();
+
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+ QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
+#endif
+}
+
+/*! \reimp
+
+ \internal
+
+ The event filter steals events from the popup or listbox when they
+ are popped up. It makes the popup stay up after a short click in
+ motif style. In windows style it toggles the arrow button of the
+ combobox field, and activates an item and takes down the listbox
+ when the mouse button is released.
+*/
+
+bool Q3ComboBox::eventFilter( QObject *object, QEvent *event )
+{
+ QStyleOptionComboBox opt = d->getStyleOption();
+ if ( !event )
+ return true;
+ else if ( object == d->ed ) {
+ if ( event->type() == QEvent::KeyPress ) {
+ bool isAccepted = ( (QKeyEvent*)event )->isAccepted();
+ keyPressEvent( (QKeyEvent *)event );
+ if ( ((QKeyEvent *)event)->isAccepted() ) {
+ d->completeNow = false;
+ return true;
+ } else if ( ((QKeyEvent *)event)->key() != Qt::Key_End ) {
+ d->completeNow = true;
+ d->completeAt = d->ed->cursorPosition();
+ }
+ if ( isAccepted )
+ ( (QKeyEvent*)event )->accept();
+ else
+ ( (QKeyEvent*)event )->ignore();
+ } else if ( event->type() == QEvent::KeyRelease ) {
+ keyReleaseEvent( (QKeyEvent *)event );
+ return ((QKeyEvent *)event)->isAccepted();
+ } else if ( event->type() == QEvent::FocusIn ) {
+ focusInEvent( (QFocusEvent *)event );
+ } else if ( event->type() == QEvent::FocusOut ) {
+ focusOutEvent( (QFocusEvent *)event );
+ } else if ( d->useCompletion && d->completeNow ) {
+ d->completeNow = false;
+ if ( !d->ed->text().isNull() &&
+ d->ed->cursorPosition() > d->completeAt &&
+ d->ed->cursorPosition() == (int)d->ed->text().length() ) {
+ QString ct( d->ed->text() );
+ int i = completionIndex( ct, currentItem() );
+ if ( i > -1 ) {
+ QString it = text( i );
+ d->ed->validateAndSet( it, ct.length(),
+ ct.length(), it.length() );
+ d->current = i;
+ // ### sets current item without emitting signals. This is to
+ // make sure the right item is current if you change current with
+ // wheel/up/down. While typing current is not valid anyway. Fix properly
+ // in 4.0.
+ }
+ }
+ }
+ } else if ( d->usingListBox() && ( object == d->listBox() ||
+ object == d->listBox()->viewport() )) {
+ QMouseEvent *e = (QMouseEvent*)event;
+ switch( event->type() ) {
+ case QEvent::MouseMove:
+ if ( !d->mouseWasInsidePopup ) {
+ QPoint pos = e->pos();
+ if ( d->listBox()->rect().contains( pos ) )
+ d->mouseWasInsidePopup = true;
+ // Check if arrow button should toggle
+ if ( d->arrowPressed ) {
+ QPoint comboPos;
+ comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) );
+ QRect arrowRect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
+ QStyle::SC_ComboBoxArrow, this);
+ if ( arrowRect.contains( comboPos ) ) {
+ if ( !d->arrowDown ) {
+ d->arrowDown = true;
+ repaint( false );
+ }
+ } else {
+ if ( d->arrowDown ) {
+ d->arrowDown = false;
+ repaint( false );
+ }
+ }
+ }
+ } else if ((e->state() & ( Qt::RightButton | Qt::LeftButton | Qt::MidButton ) ) == 0 &&
+ style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, this)) {
+ QWidget *mouseW = QApplication::widgetAt( e->globalPos(), true );
+ if ( mouseW == d->listBox()->viewport() ) { //###
+ QMouseEvent m( QEvent::MouseMove, e->pos(), e->globalPos(),
+ Qt::NoButton, Qt::LeftButton );
+ QApplication::sendEvent( object, &m ); //### Evil
+ return true;
+ }
+ }
+
+ break;
+ case QEvent::MouseButtonRelease:
+ if ( d->listBox()->rect().contains( e->pos() ) ) {
+ QMouseEvent tmp( QEvent::MouseButtonDblClick,
+ e->pos(), e->button(), e->state() ) ;
+ // will hide popup
+ QApplication::sendEvent( object, &tmp );
+ return true;
+ } else {
+ if ( d->mouseWasInsidePopup ) {
+ popDownListBox();
+ } else {
+ d->arrowPressed = false;
+ if ( d->arrowDown ) {
+ d->arrowDown = false;
+ repaint( false );
+ }
+ }
+ }
+ break;
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ if ( !d->listBox()->rect().contains( e->pos() ) ) {
+ QPoint globalPos = d->listBox()->mapToGlobal(e->pos());
+ if ( QApplication::widgetAt( globalPos, true ) == this ) {
+ d->discardNextMousePress = true;
+ // avoid popping up again
+ }
+ popDownListBox();
+ return true;
+ }
+ break;
+ case QEvent::KeyPress:
+ switch( ((QKeyEvent *)event)->key() ) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ if ( !(((QKeyEvent *)event)->state() & Qt::AltModifier) )
+ break;
+ case Qt::Key_F4:
+ case Qt::Key_Escape:
+ if ( d->poppedUp ) {
+ popDownListBox();
+ return true;
+ }
+ break;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ // work around QDialog's enter handling
+ return false;
+ default:
+ break;
+ }
+ break;
+ case QEvent::Hide:
+ popDownListBox();
+ break;
+ default:
+ break;
+ }
+ } else if ( (!d->usingListBox() || style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) &&
+ object == d->popup() ) {
+ QMouseEvent *e = (QMouseEvent*)event;
+ switch ( event->type() ) {
+ case QEvent::MouseButtonRelease:
+ if ( d->shortClick ) {
+ QMouseEvent tmp( QEvent::MouseMove,
+ e->pos(), e->button(), e->state() ) ;
+ // highlight item, but don't pop down:
+ QApplication::sendEvent( object, &tmp );
+ return true;
+ }
+ break;
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ if ( !d->popup()->rect().contains( e->pos() ) ) {
+ d->poppedUp = false;
+ d->arrowDown = false;
+ // remove filter, event will take down popup:
+ d->popup()->removeEventFilter( this );
+ // ### uglehack!
+ // call internalHighlight so the highlighed signal
+ // will be emitted at least as often as necessary.
+ // it may be called more often than necessary
+ internalHighlight( d->current );
+ }
+ break;
+ case QEvent::Hide:
+ d->poppedUp = false;
+ break;
+ default:
+ break;
+ }
+ }
+ return QWidget::eventFilter( object, event );
+}
+
+
+/*!
+ Returns the index of the first item \e after \a startingAt of
+ which \a prefix is a case-insensitive prefix. Returns -1 if no
+ items start with \a prefix.
+*/
+
+int Q3ComboBox::completionIndex( const QString & prefix,
+ int startingAt = 0 ) const
+{
+ int start = startingAt;
+ if ( start < 0 || start >= count() )
+ start = 0;
+ if ( start >= count() )
+ return -1;
+ QString match = prefix.lower();
+ if ( match.length() < 1 )
+ return start;
+
+ QString current;
+ int i = start;
+ do {
+ current = text( i ).lower();
+ if ( current.startsWith( match ) )
+ return i;
+ i++;
+ if ( i == count() )
+ i = 0;
+ } while ( i != start );
+ return -1;
+}
+
+int Q3ComboBox::sizeLimit() const
+{
+ return d ? d->sizeLimit : INT_MAX;
+}
+
+void Q3ComboBox::setSizeLimit( int lines )
+{
+ d->sizeLimit = lines;
+}
+
+
+int Q3ComboBox::maxCount() const
+{
+ return d ? d->maxCount : INT_MAX;
+}
+
+void Q3ComboBox::setMaxCount( int count )
+{
+ int l = this->count();
+ while( --l > count )
+ removeItem( l );
+ d->maxCount = count;
+}
+
+Q3ComboBox::Policy Q3ComboBox::insertionPolicy() const
+{
+ return d->p;
+}
+
+void Q3ComboBox::setInsertionPolicy( Policy policy )
+{
+ d->p = policy;
+}
+
+
+
+/*!
+ Internal slot to keep the line editor up to date.
+*/
+
+void Q3ComboBox::returnPressed()
+{
+ QString s( d->ed->text() );
+
+ if ( s.isEmpty() )
+ return;
+
+ int c = 0;
+ bool doInsert = true;
+ if ( !d->duplicatesEnabled ) {
+ for ( int i = 0; i < count(); ++i ) {
+ if ( s == text( i ) ) {
+ doInsert = false;
+ c = i;
+ break;
+ }
+ }
+ }
+
+ if ( doInsert ) {
+ if ( insertionPolicy() != NoInsert ) {
+ int cnt = count();
+ while ( cnt >= d->maxCount ) {
+ removeItem( --cnt );
+ }
+ }
+
+ switch ( insertionPolicy() ) {
+ case InsertAtCurrent:
+ if (count() == 0)
+ insertItem(s);
+ else if ( s != text( currentItem() ) )
+ changeItem( s, currentItem() );
+ emit activated( currentItem() );
+ emit activated( s );
+ return;
+ case NoInsert:
+ emit activated( s );
+ return;
+ case InsertAtTop:
+ c = 0;
+ break;
+ case InsertAtBottom:
+ c = count();
+ break;
+ case InsertBeforeCurrent:
+ c = currentItem();
+ break;
+ case InsertAfterCurrent:
+ c = count() == 0 ? 0 : currentItem() + 1;
+ break;
+ }
+ insertItem( s, c );
+ }
+
+ setCurrentItem( c );
+ emit activated( c );
+ emit activated( s );
+}
+
+
+/*!
+ Enables the combobox if \a enable is true; otherwise disables it.
+
+ \sa QWidget::enabled
+*/
+
+void Q3ComboBox::setEnabled( bool enable )
+{
+ if ( !enable ) {
+ if ( d->usingListBox() ) {
+ popDownListBox();
+ } else {
+ d->popup()->removeEventFilter( this );
+ d->popup()->close();
+ d->poppedUp = false;
+ }
+ }
+ QWidget::setEnabled( enable );
+}
+
+
+
+/*!
+ Applies the validator \a v to the combobox so that only text which
+ is valid according to \a v is accepted.
+
+ This function does nothing if the combobox is not editable.
+
+ \sa validator() clearValidator() QValidator
+*/
+
+void Q3ComboBox::setValidator( const QValidator * v )
+{
+ if ( d && d->ed )
+ d->ed->setValidator( v );
+}
+
+
+/*!
+ Returns the validator which constrains editing for this combobox
+ if there is one; otherwise returns 0.
+
+ \sa setValidator() clearValidator() QValidator
+*/
+
+const QValidator * Q3ComboBox::validator() const
+{
+ return d && d->ed ? d->ed->validator() : 0;
+}
+
+
+/*!
+ This slot is equivalent to setValidator( 0 ).
+*/
+
+void Q3ComboBox::clearValidator()
+{
+ if ( d && d->ed )
+ d->ed->setValidator( 0 );
+}
+
+
+/*!
+ Sets the combobox to use \a newListBox instead of the current list
+ box or popup. As a side effect, it clears the combobox of its
+ current contents.
+
+ \warning Q3ComboBox assumes that newListBox->text(n) returns
+ non-null for 0 \<= n \< newListbox->count(). This assumption is
+ necessary because of the line edit in Q3ComboBox.
+*/
+
+void Q3ComboBox::setListBox( Q3ListBox * newListBox )
+{
+ clear();
+
+ if ( d->usingListBox() ) {
+ delete d->listBox();
+ } else {
+ delete d->popup();
+ d->setPopupMenu(0, false);
+ }
+
+ newListBox->reparent( this, Qt::WType_Popup, QPoint(0,0), false );
+ d->setListBox( newListBox );
+ d->listBox()->setFont( font() );
+ d->listBox()->setPalette( palette() );
+ d->listBox()->setVScrollBarMode(Q3ScrollView::AlwaysOff);
+ d->listBox()->setHScrollBarMode(Q3ScrollView::AlwaysOff);
+ d->listBox()->setFrameStyle( Q3Frame::Box | Q3Frame::Plain );
+ d->listBox()->setLineWidth( 1 );
+ d->listBox()->resize( 100, 10 );
+
+ connect( d->listBox(), SIGNAL(selected(int)),
+ SLOT(internalActivate(int)) );
+ connect( d->listBox(), SIGNAL(highlighted(int)),
+ SLOT(internalHighlight(int)));
+}
+
+
+/*!
+ Returns the current list box, or 0 if there is no list box.
+ (Q3ComboBox can use QPopupMenu instead of QListBox.) Provided to
+ match setListBox().
+
+ \sa setListBox()
+*/
+
+Q3ListBox * Q3ComboBox::listBox() const
+{
+ return d && d->usingListBox() ? d->listBox() : 0;
+}
+
+/*!
+ Returns the line edit, or 0 if there is no line edit.
+
+ Only editable listboxes have a line editor.
+*/
+QLineEdit* Q3ComboBox::lineEdit() const
+{
+ return d->ed;
+}
+
+
+
+/*!
+ Clears the line edit without changing the combobox's contents.
+ Does nothing if the combobox isn't editable.
+
+ This is particularly useful when using a combobox as a line edit
+ with history. For example you can connect the combobox's
+ activated() signal to clearEdit() in order to present the user
+ with a new, empty line as soon as Enter is pressed.
+
+ \sa setEditText()
+*/
+
+void Q3ComboBox::clearEdit()
+{
+ if ( d && d->ed )
+ d->ed->clear();
+}
+
+
+/*!
+ Sets the text in the line edit to \a newText without changing the
+ combobox's contents. Does nothing if the combobox isn't editable.
+
+ This is useful e.g. for providing a good starting point for the
+ user's editing and entering the change in the combobox only when
+ the user presses Enter.
+
+ \sa clearEdit() insertItem()
+*/
+
+void Q3ComboBox::setEditText( const QString &newText )
+{
+ if ( d && d->ed ) {
+ d->updateLinedGeometry();
+ d->ed->setText( newText );
+ }
+}
+
+void Q3ComboBox::setAutoCompletion( bool enable )
+{
+ d->useCompletion = enable;
+ d->completeNow = false;
+}
+
+
+bool Q3ComboBox::autoCompletion() const
+{
+ return d->useCompletion;
+}
+
+/*!
+ \internal
+ */
+void Q3ComboBox::styleChange( QStyle& s )
+{
+ d->sizeHint = QSize(); // invalidate size hint...
+ if ( d->ed )
+ d->updateLinedGeometry();
+ QWidget::styleChange( s );
+}
+
+bool Q3ComboBox::editable() const
+{
+ return d->ed != 0;
+}
+
+void Q3ComboBox::setEditable( bool y )
+{
+ if ( y == editable() )
+ return;
+ if ( y ) {
+ if ( !d->usingListBox() )
+ setUpListBox();
+ setUpLineEdit();
+ d->ed->show();
+ if ( currentItem() )
+ setEditText( currentText() );
+ } else {
+ delete d->ed;
+ d->ed = 0;
+ }
+
+ setFocusPolicy(Qt::StrongFocus);
+ updateGeometry();
+ update();
+}
+
+
+void Q3ComboBox::setUpListBox()
+{
+ d->setListBox( new Q3ListBox( this, "in-combo", Qt::WType_Popup ) );
+ d->listBox()->setFont( font() );
+ d->listBox()->setPalette( palette() );
+ d->listBox()->setVScrollBarMode( Q3ListBox::AlwaysOff );
+ d->listBox()->setHScrollBarMode( Q3ListBox::AlwaysOff );
+ d->listBox()->setFrameStyle( Q3Frame::Box | Q3Frame::Plain );
+ d->listBox()->setLineWidth( 1 );
+ d->listBox()->resize( 100, 10 );
+
+ connect( d->listBox(), SIGNAL(selected(int)),
+ SLOT(internalActivate(int)) );
+ connect( d->listBox(), SIGNAL(highlighted(int)),
+ SLOT(internalHighlight(int)));
+}
+
+
+void Q3ComboBox::setUpLineEdit()
+{
+ if ( !d->ed )
+ setLineEdit( new QLineEdit( this, "combo edit" ) );
+}
+
+/*!
+ Sets the line edit to use \a edit instead of the current line edit.
+*/
+
+void Q3ComboBox::setLineEdit( QLineEdit *edit )
+{
+ if ( !edit ) {
+#if defined(QT_CHECK_NULL)
+ Q_ASSERT( edit != 0 );
+#endif
+ return;
+ }
+
+ edit->setText( currentText() );
+ delete d->ed;
+ d->ed = edit;
+
+ if ( edit->parent() != this )
+ edit->reparent( this, QPoint(0,0), false );
+
+ connect (edit, SIGNAL(textChanged(QString)),
+ this, SIGNAL(textChanged(QString)) );
+ connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) );
+
+ edit->setFrame( false );
+ d->updateLinedGeometry();
+ edit->installEventFilter( this );
+ setFocusProxy( edit );
+ setFocusPolicy(Qt::StrongFocus);
+ setInputMethodEnabled( true );
+
+ if ( !d->usingListBox() )
+ setUpListBox();
+
+ if ( isVisible() )
+ edit->show();
+
+ updateGeometry();
+ update();
+}
+
+/*!
+ Hides the combobox.
+
+ \sa QWidget::hide()
+*/
+void Q3ComboBox::hide()
+{
+ QWidget::hide();
+
+ if (listBox())
+ listBox()->hide();
+ else if (d->popup())
+ d->popup()->hide();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_COMBOBOX
diff --git a/src/qt3support/widgets/q3combobox.h b/src/qt3support/widgets/q3combobox.h
new file mode 100644
index 0000000..813831e
--- /dev/null
+++ b/src/qt3support/widgets/q3combobox.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3COMBOBOX_H
+#define Q3COMBOBOX_H
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_COMBOBOX
+
+class Q3StrList;
+class QStringList;
+class QLineEdit;
+class QValidator;
+class Q3ListBox;
+class Q3ComboBoxData;
+class QWheelEvent;
+
+class Q_COMPAT_EXPORT Q3ComboBox : public QWidget
+{
+ Q_OBJECT
+ Q_ENUMS( Policy )
+ Q_PROPERTY( bool editable READ editable WRITE setEditable )
+ Q_PROPERTY( int count READ count )
+ Q_PROPERTY( QString currentText READ currentText WRITE setCurrentText DESIGNABLE false )
+ Q_PROPERTY( int currentItem READ currentItem WRITE setCurrentItem )
+ Q_PROPERTY( bool autoResize READ autoResize WRITE setAutoResize DESIGNABLE false )
+ Q_PROPERTY( int sizeLimit READ sizeLimit WRITE setSizeLimit )
+ Q_PROPERTY( int maxCount READ maxCount WRITE setMaxCount )
+ Q_PROPERTY( Policy insertionPolicy READ insertionPolicy WRITE setInsertionPolicy )
+ Q_PROPERTY( bool autoCompletion READ autoCompletion WRITE setAutoCompletion )
+ Q_PROPERTY( bool duplicatesEnabled READ duplicatesEnabled WRITE setDuplicatesEnabled )
+
+public:
+ Q3ComboBox( QWidget* parent=0, const char* name=0 );
+ Q3ComboBox( bool rw, QWidget* parent=0, const char* name=0 );
+ ~Q3ComboBox();
+
+ int count() const;
+
+ void insertStringList( const QStringList &, int index=-1 );
+ void insertStrList( const Q3StrList &, int index=-1 );
+ void insertStrList( const Q3StrList *, int index=-1 );
+ void insertStrList( const char **, int numStrings=-1, int index=-1);
+
+ void insertItem( const QString &text, int index=-1 );
+ void insertItem( const QPixmap &pixmap, int index=-1 );
+ void insertItem( const QPixmap &pixmap, const QString &text, int index=-1 );
+
+ void removeItem( int index );
+
+ int currentItem() const;
+ virtual void setCurrentItem( int index );
+
+ QString currentText() const;
+ virtual void setCurrentText( const QString& );
+
+ QString text( int index ) const;
+ const QPixmap *pixmap( int index ) const;
+
+ void changeItem( const QString &text, int index );
+ void changeItem( const QPixmap &pixmap, int index );
+ void changeItem( const QPixmap &pixmap, const QString &text, int index );
+
+ bool autoResize() const;
+ virtual void setAutoResize( bool );
+ QSize sizeHint() const;
+
+ void setPalette( const QPalette & );
+ void setFont( const QFont & );
+ void setEnabled( bool );
+
+ virtual void setSizeLimit( int );
+ int sizeLimit() const;
+
+ virtual void setMaxCount( int );
+ int maxCount() const;
+
+ enum Policy { NoInsertion,
+ AtTop,
+ AtCurrent,
+ AtBottom,
+ AfterCurrent,
+ BeforeCurrent,
+ NoInsert = NoInsertion,
+ InsertAtTop = AtTop,
+ InsertAtCurrent = AtCurrent,
+ InsertAtBottom = AtBottom,
+ InsertAfterCurrent = AfterCurrent,
+ InsertBeforeCurrent = BeforeCurrent
+ };
+
+ virtual void setInsertionPolicy( Policy policy );
+ Policy insertionPolicy() const;
+
+ virtual void setValidator( const QValidator * );
+ const QValidator * validator() const;
+
+ virtual void setListBox( Q3ListBox * );
+ Q3ListBox * listBox() const;
+
+ virtual void setLineEdit( QLineEdit *edit );
+ QLineEdit* lineEdit() const;
+
+ virtual void setAutoCompletion( bool );
+ bool autoCompletion() const;
+
+ bool eventFilter( QObject *object, QEvent *event );
+
+ void setDuplicatesEnabled( bool enable );
+ bool duplicatesEnabled() const;
+
+ bool editable() const;
+ void setEditable( bool );
+
+ virtual void popup();
+
+ void hide();
+
+public Q_SLOTS:
+ void clear();
+ void clearValidator();
+ void clearEdit();
+ virtual void setEditText( const QString &);
+
+Q_SIGNALS:
+ void activated( int index );
+ void highlighted( int index );
+ void activated( const QString &);
+ void highlighted( const QString &);
+ void textChanged( const QString &);
+
+private Q_SLOTS:
+ void internalActivate( int );
+ void internalHighlight( int );
+ void internalClickTimeout();
+ void returnPressed();
+
+protected:
+ void paintEvent( QPaintEvent * );
+ void resizeEvent( QResizeEvent * );
+ void mousePressEvent( QMouseEvent * );
+ void mouseMoveEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+ void mouseDoubleClickEvent( QMouseEvent * );
+ void keyPressEvent( QKeyEvent *e );
+ void focusInEvent( QFocusEvent *e );
+ void focusOutEvent( QFocusEvent *e );
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent( QWheelEvent *e );
+#endif
+ void styleChange( QStyle& );
+
+ void updateMask();
+
+private:
+ void setUpListBox();
+ void setUpLineEdit();
+ void popDownListBox();
+ void reIndex();
+ void currentChanged();
+ int completionIndex( const QString &, int ) const;
+
+ Q3ComboBoxData *d;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3ComboBox( const Q3ComboBox & );
+ Q3ComboBox &operator=( const Q3ComboBox & );
+#endif
+};
+
+
+#endif // QT_NO_COMBOBOX
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3COMBOBOX_H
diff --git a/src/qt3support/widgets/q3datetimeedit.cpp b/src/qt3support/widgets/q3datetimeedit.cpp
new file mode 100644
index 0000000..6d35523
--- /dev/null
+++ b/src/qt3support/widgets/q3datetimeedit.cpp
@@ -0,0 +1,2807 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3datetimeedit.h"
+
+#ifndef QT_NO_DATETIMEEDIT
+
+#include <private/q3richtext_p.h>
+#include "qevent.h"
+#include "q3rangecontrol.h"
+#include "qapplication.h"
+#include "qpixmap.h"
+#include "qlist.h"
+#include "qstring.h"
+#include "qstyle.h"
+
+#if defined(Q_WS_WIN)
+#include "qt_windows.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define QDATETIMEEDIT_HIDDEN_CHAR QLatin1Char('0')
+
+class Q_COMPAT_EXPORT QNumberSection
+{
+public:
+ QNumberSection(int selStart = 0, int selEnd = 0, bool separat = true, int actual = -1)
+ : selstart(selStart), selend(selEnd), act(actual), sep(separat)
+ {}
+ int selectionStart() const { return selstart; }
+ void setSelectionStart(int s) { selstart = s; }
+ int selectionEnd() const { return selend; }
+ void setSelectionEnd(int s) { selend = s; }
+ int width() const { return selend - selstart; }
+ int index() const { return act; }
+ bool separator() const { return sep; }
+ Q_DUMMY_COMPARISON_OPERATOR(QNumberSection)
+private:
+ signed int selstart :12;
+ signed int selend :12;
+ signed int act :7;
+ bool sep :1;
+};
+
+static QString *lDateSep = 0;
+static QString *lTimeSep = 0;
+static bool lAMPM = false;
+static QString *lAM = 0;
+static QString *lPM = 0;
+static Q3DateEdit::Order lOrder = Q3DateEdit::YMD;
+static int refcount = 0;
+
+static void cleanup()
+{
+ delete lDateSep;
+ lDateSep = 0;
+ delete lTimeSep;
+ lTimeSep = 0;
+ delete lAM;
+ lAM = 0;
+ delete lPM;
+ lPM = 0;
+}
+
+/*!
+\internal
+try to get the order of DMY and the date/time separator from the locale settings
+*/
+static void readLocaleSettings()
+{
+ int dpos, mpos, ypos;
+ cleanup();
+
+ lDateSep = new QString();
+ lTimeSep = new QString();
+
+#if defined(Q_WS_WIN)
+ wchar_t data[10];
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, data, 10);
+ *lDateSep = QString::fromWCharArray(data);
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIME, data, 10);
+ *lTimeSep = QString::fromWCharArray(data);
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, data, 10);
+ lAMPM = QString::fromWCharArray(data).toInt() == 0;
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S1159, data, 10);
+ QString am = QString::fromWCharArray(data);
+ if (!am.isEmpty())
+ lAM = new QString(am);
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S2359, data, 10);
+ QString pm = QString::fromWCharArray(data);
+ if (!pm.isEmpty() )
+ lPM = new QString(pm);
+#else
+ *lDateSep = QLatin1Char('-');
+ *lTimeSep = QLatin1Char(':');
+#endif
+ QString d = QDate(1999, 11, 22).toString(Qt::LocalDate);
+ dpos = d.indexOf(QLatin1String("22"));
+ mpos = d.indexOf(QLatin1String("11"));
+ ypos = d.indexOf(QLatin1String("99"));
+ if (dpos > -1 && mpos > -1 && ypos > -1) {
+ // test for DMY, MDY, YMD, YDM
+ if (dpos < mpos && mpos < ypos) {
+ lOrder = Q3DateEdit::DMY;
+ } else if (mpos < dpos && dpos < ypos) {
+ lOrder = Q3DateEdit::MDY;
+ } else if (ypos < mpos && mpos < dpos) {
+ lOrder = Q3DateEdit::YMD;
+ } else if (ypos < dpos && dpos < mpos) {
+ lOrder = Q3DateEdit::YDM;
+ } else {
+ // cannot determine the dateformat - use the default
+ return;
+ }
+
+ // this code needs to change if new formats are added
+
+#ifndef Q_WS_WIN
+ QString sep = d.mid(qMin(dpos, mpos) + 2, QABS(dpos - mpos) - 2);
+ if (d.count(sep) == 2) {
+ *lDateSep = sep;
+ }
+#endif
+ }
+
+#ifndef Q_WS_WIN
+ QString t = QTime(11, 22, 33).toString(Qt::LocalDate);
+ dpos = t.indexOf(QLatin1String("11"));
+ mpos = t.indexOf(QLatin1String("22"));
+ ypos = t.indexOf(QLatin1String("33"));
+ // We only allow hhmmss
+ if (dpos > -1 && dpos < mpos && mpos < ypos) {
+ QString sep = t.mid(dpos + 2, mpos - dpos - 2);
+ if (sep == t.mid(mpos + 2, ypos - mpos - 2)) {
+ *lTimeSep = sep;
+ }
+ }
+#endif
+}
+
+static Q3DateEdit::Order localOrder() {
+ if (!lDateSep) {
+ readLocaleSettings();
+ }
+ return lOrder;
+}
+
+static QString localDateSep() {
+ if (!lDateSep) {
+ readLocaleSettings();
+ }
+ return *lDateSep;
+}
+
+static QString localTimeSep() {
+ if (!lTimeSep) {
+ readLocaleSettings();
+ }
+ return *lTimeSep;
+}
+
+class Q3DateTimeEditorPrivate
+{
+public:
+ Q3DateTimeEditorPrivate()
+ : frm(true),
+ parag(new Q3TextParagraph(0, 0, 0, false)),
+ focusSec(0)
+ {
+ parag->formatter()->setWrapEnabled(false);
+ cursor = new Q3TextCursor(0);
+ cursor->setParagraph(parag);
+ offset = 0;
+ sep = localDateSep();
+ refcount++;
+ }
+ ~Q3DateTimeEditorPrivate()
+ {
+ delete parag;
+ delete cursor;
+ if (!--refcount)
+ cleanup();
+ }
+
+ void appendSection(const QNumberSection& sec)
+ {
+ sections.append(sec);
+
+ }
+ void clearSections()
+ {
+ sections.clear();
+ }
+ void setSectionSelection(int sec, int selstart, int selend)
+ {
+ if (sec < 0 || sec >= sections.count())
+ return;
+ sections[sec].setSelectionStart(selstart);
+ sections[sec].setSelectionEnd(selend);
+ }
+ uint sectionCount() const { return (uint)sections.count(); }
+ void setSeparator(const QString& s) { sep = s; }
+ QString separator() const { return sep; }
+
+ void setFrame(bool f) { frm = f; }
+ bool frame() const { return frm; }
+
+ int focusSection() const { return focusSec; }
+ int section(const QPoint& p)
+ {
+ cursor->place(p + QPoint(offset, 0), parag);
+ int idx = cursor->index();
+ for (int i = 0; i < sections.count(); ++i) {
+ if (idx >= sections[i].selectionStart() &&
+ idx <= sections[i].selectionEnd())
+ return i;
+ }
+ return -1;
+ }
+ QNumberSection section(int idx) const
+ {
+ return sections[idx];
+ }
+ bool setFocusSection(int idx)
+ {
+ if (idx > (int)sections.count()-1 || idx < 0)
+ return false;
+ if (idx != focusSec) {
+ focusSec = idx;
+ applyFocusSelection();
+ return true;
+ }
+ return false;
+ }
+
+ bool inSectionSelection(int idx)
+ {
+ for (int i = 0; i < sections.count(); ++i) {
+ if (idx >= sections[i].selectionStart() &&
+ idx <= sections[i].selectionEnd())
+ return true;
+ }
+ return false;
+ }
+
+ void paint(const QString& txt, bool focus, QPainter& p,
+ const QPalette&pal, const QRect& rect, QStyle *style)
+ {
+ int fw = 0;
+ if (frm)
+ fw = style->pixelMetric(QStyle::PM_DefaultFrameWidth);
+
+ parag->truncate(0);
+ parag->append(txt);
+ if (!focus)
+ parag->removeSelection(Q3TextDocument::Standard);
+ else {
+ applyFocusSelection();
+ }
+
+ /* color all QDATETIMEEDIT_HIDDEN_CHAR chars to background color */
+ Q3TextFormat *fb = parag->formatCollection()->format(p.font(),
+ pal.base().color());
+ Q3TextFormat *nf = parag->formatCollection()->format(p.font(),
+ pal.text().color());
+ for (int i = 0; i < txt.length(); ++i) {
+ parag->setFormat(i, 1, nf);
+ if (inSectionSelection(i))
+ continue;
+ if (txt.at(i) == QDATETIMEEDIT_HIDDEN_CHAR)
+ parag->setFormat(i, 1, fb);
+ else
+ parag->setFormat(i, 1, nf);
+ }
+ fb->removeRef();
+ nf->removeRef();
+
+ QRect r(rect.x(), rect.y(), rect.width() - 2 * (2 + fw), rect.height());
+ parag->pseudoDocument()->docRect = r;
+ parag->invalidate(0);
+ parag->format();
+
+ int xoff = 2 + fw - offset;
+ int yoff = (rect.height() - parag->rect().height() + 1) / 2;
+ if (yoff < 0)
+ yoff = 0;
+
+ p.translate(xoff, yoff);
+ parag->paint(p, pal, 0, true);
+ if (frm)
+ p.translate(-xoff, -yoff);
+ }
+
+ void resize(const QSize& size) { sz = size; }
+
+ int mapSection(int sec)
+ {
+ return (sec >= 0 && sec < sections.count() ? sections[sec].index() : -1);
+ }
+
+protected:
+ void applyFocusSelection()
+ {
+ if (focusSec > -1 && focusSec < sections.count()) {
+ int selstart = sections[focusSec].selectionStart();
+ int selend = sections[focusSec].selectionEnd();
+ parag->setSelection(Q3TextDocument::Standard, selstart, selend);
+ parag->format();
+ if (parag->at(selstart)->x < offset ||
+ parag->at(selend)->x + parag->string()->width(selend) > offset + sz.width()) {
+ offset = parag->at(selstart)->x;
+ }
+ }
+ }
+private:
+ bool frm;
+ Q3TextParagraph *parag;
+ Q3TextCursor *cursor;
+ QSize sz;
+ int focusSec;
+ QList< QNumberSection > sections;
+ QString sep;
+ int offset;
+};
+
+class Q3DateTimeEditor : public QWidget
+{
+ Q_OBJECT
+public:
+ Q3DateTimeEditor(Q3DateTimeEditBase *widget, QWidget *parent, const char* name=0);
+ ~Q3DateTimeEditor();
+
+ void setControlWidget(Q3DateTimeEditBase * widget);
+ Q3DateTimeEditBase * controlWidget() const;
+
+ void setSeparator(const QString& s);
+ QString separator() const;
+
+ int focusSection() const;
+ bool setFocusSection(int s);
+ void appendSection(const QNumberSection& sec);
+ void clearSections();
+ void setSectionSelection(int sec, int selstart, int selend);
+ bool eventFilter(QObject *o, QEvent *e);
+ int sectionAt(const QPoint &p);
+ int mapSection(int sec);
+
+protected:
+ void init();
+ bool event(QEvent *e);
+ void resizeEvent(QResizeEvent *);
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *e);
+
+private:
+ Q3DateTimeEditBase* cw;
+ Q3DateTimeEditorPrivate* d;
+};
+
+class QDateTimeSpinWidget : public Q3SpinWidget
+{
+ Q_OBJECT
+public:
+ QDateTimeSpinWidget(QWidget *parent, const char *name)
+ : Q3SpinWidget(parent, name)
+ {
+ }
+
+ void changeEvent(QEvent *e)
+ {
+ if (e->type() == QEvent::EnabledChange && isEnabled()) {
+ Q3DateEdit *de = qobject_cast<Q3DateEdit*>(parentWidget());
+ if (de) {
+ setUpEnabled(de->date() < de->maxValue());
+ setDownEnabled(de->date() > de->minValue());
+ } else {
+ setUpEnabled(true);
+ setDownEnabled(true);
+ }
+ }
+ }
+ void enabledChange(bool notenabled)
+ {
+ Q3DateEdit *de = qobject_cast<Q3DateEdit*>(parentWidget());
+ if (de && !notenabled) {
+ setUpEnabled(de->date() < de->maxValue());
+ setDownEnabled(de->date() > de->minValue());
+ } else {
+ setUpEnabled(!notenabled);
+ setDownEnabled(!notenabled);
+ }
+ }
+
+
+protected:
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *e)
+ {
+ Q3DateTimeEditor *editor = qobject_cast<Q3DateTimeEditor*>(editWidget());
+ Q_ASSERT(editor);
+ if (!editor)
+ return;
+
+ int section = editor->sectionAt(e->pos());
+ editor->setFocusSection(section);
+
+ if (section == -1)
+ return;
+ Q3SpinWidget::wheelEvent(e);
+ }
+#endif
+};
+
+/*!
+ Constructs an empty datetime editor with parent \a parent and
+ called \a name.
+*/
+Q3DateTimeEditor::Q3DateTimeEditor(Q3DateTimeEditBase *widget, QWidget *parent, const char * name)
+ : QWidget(parent, name)
+{
+ d = new Q3DateTimeEditorPrivate();
+ cw = widget;
+ init();
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3DateTimeEditor::~Q3DateTimeEditor()
+{
+ delete d;
+}
+
+/*! \internal
+
+*/
+
+void Q3DateTimeEditor::init()
+{
+ setBackgroundRole(QPalette::Base);
+ setFocusSection(-1);
+ installEventFilter(this);
+ setFocusPolicy(Qt::WheelFocus);
+}
+
+
+/*! \reimp
+
+*/
+
+bool Q3DateTimeEditor::event(QEvent *e)
+{
+ if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) {
+ if (e->type() == QEvent::FocusOut)
+ qApp->sendEvent(cw, e);
+ update(rect());
+ } else if (e->type() == QEvent::ShortcutOverride) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ switch (ke->key()) {
+ case Qt::Key_Delete:
+ case Qt::Key_Backspace:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ return QWidget::event(e);
+}
+
+/*! \reimp
+
+*/
+
+void Q3DateTimeEditor::resizeEvent(QResizeEvent *e)
+{
+ d->resize(e->size());
+ QWidget::resizeEvent(e);
+}
+
+
+/*! \reimp
+
+*/
+
+void Q3DateTimeEditor::paintEvent(QPaintEvent *)
+{
+ QString txt;
+ for (uint i = 0; i < d->sectionCount(); ++i) {
+ txt += cw->sectionFormattedText(i);
+ if (i < d->sectionCount()-1) {
+ if (d->section(i+1).separator())
+ txt += d->separator();
+ else
+ txt += QLatin1Char(' ');
+ }
+ }
+
+ QPainter p(this);
+ const QBrush &bg = palette().brush(isEnabled() ? QPalette::Base : QPalette::Window);
+ p.fillRect(0, 0, width(), height(), bg);
+ d->paint(txt, hasFocus(), p, palette(), rect(), style());
+}
+
+
+/*!
+ Returns the section index at point \a p.
+*/
+int Q3DateTimeEditor::sectionAt(const QPoint &p)
+{
+ return d->section(p);
+}
+
+int Q3DateTimeEditor::mapSection(int sec)
+{
+ return d->mapSection(sec);
+}
+
+
+/*! \reimp
+
+*/
+
+void Q3DateTimeEditor::mousePressEvent(QMouseEvent *e)
+{
+ QPoint p(e->pos().x(), 0);
+ int sec = sectionAt(p);
+ if (sec != -1) {
+ cw->setFocusSection(sec);
+ repaint(rect());
+ }
+}
+
+/*! \reimp
+
+*/
+bool Q3DateTimeEditor::eventFilter(QObject *o, QEvent *e)
+{
+ if (o == this) {
+ if (e->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = (QKeyEvent*)e;
+ switch (ke->key()) {
+ case Qt::Key_Right:
+ if (d->focusSection() < (int)d->sectionCount()-1) {
+ if (cw->setFocusSection(focusSection()+1))
+ repaint(rect());
+ }
+ return true;
+ case Qt::Key_Left:
+ if (d->focusSection() > 0) {
+ if (cw->setFocusSection(focusSection()-1))
+ repaint(rect());
+ }
+ return true;
+ case Qt::Key_Up:
+ cw->stepUp();
+ return true;
+ case Qt::Key_Down:
+ cw->stepDown();
+ return true;
+ case Qt::Key_Backspace:
+ if (qobject_cast<Q3DateEdit*>(cw))
+ ((Q3DateEdit*)cw)->removeFirstNumber(d->focusSection());
+ else if (qobject_cast<Q3TimeEdit*>(cw))
+ ((Q3TimeEdit*)cw)->removeFirstNumber(d->focusSection());
+ return true;
+ case Qt::Key_Delete:
+ cw->removeLastNumber(d->focusSection());
+ return true;
+ case Qt::Key_Tab:
+ case Qt::Key_BackTab: {
+ if (ke->state() == Qt::ControlButton)
+ return false;
+ QWidget *w = this;
+ bool hadDateEdit = false;
+ while (w) {
+ if (qobject_cast<QDateTimeSpinWidget*>(w) || qobject_cast<Q3DateTimeEdit*>(w))
+ break;
+ hadDateEdit = hadDateEdit || qobject_cast<Q3DateEdit*>(w);
+ w = w->parentWidget();
+ }
+ if (w) {
+ if (!qobject_cast<Q3DateTimeEdit*>(w)) {
+ w = w->parentWidget();
+ } else {
+ Q3DateTimeEdit *ed = (Q3DateTimeEdit*)w;
+ if (hadDateEdit && ke->key() == Qt::Key_Tab) {
+ ed->timeEdit()->setFocus();
+ return true;
+ } else if (!hadDateEdit && ke->key() == Qt::Key_BackTab) {
+ ed->dateEdit()->setFocus();
+ return true;
+ } else {
+ while (w && !qobject_cast<Q3DateTimeEdit*>(w))
+ w = w->parentWidget();
+ }
+ }
+ qApp->sendEvent(w, e);
+ return true;
+ }
+ } break;
+ default:
+ QString txt = ke->text().toLower();
+ if (!txt.isEmpty() && !separator().isEmpty() && txt[0] == separator()[0]) {
+ // do the same thing as KEY_RIGHT when the user presses the separator key
+ if (d->focusSection() < 2) {
+ if (cw->setFocusSection(focusSection()+1))
+ repaint(rect());
+ }
+ return true;
+ } else if (!txt.isEmpty() && qobject_cast<Q3TimeEdit*>(cw) && focusSection() == (int) d->sectionCount()-1) {
+ // the first character of the AM/PM indicator toggles if the section has focus
+ Q3TimeEdit *te = (Q3TimeEdit*)cw;
+ QTime time = te->time();
+ if (lAMPM && lAM && lPM && (te->display()&Q3TimeEdit::AMPM)) {
+ if (txt[0] == (*lAM).toLower()[0] && time.hour() >= 12) {
+ time.setHMS(time.hour()-12, time.minute(), time.second(), time.msec());
+ te->setTime(time);
+ } else if (txt[0] == (*lPM).toLower()[0] && time.hour() < 12) {
+ time.setHMS(time.hour()+12, time.minute(), time.second(), time.msec());
+ te->setTime(time);
+ }
+ }
+ }
+
+ int num = txt[0].digitValue();
+ if (num != -1) {
+ cw->addNumber(d->focusSection(), num);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+/*!
+ Appends the number section \a sec to the editor.
+*/
+
+void Q3DateTimeEditor::appendSection(const QNumberSection& sec)
+{
+ d->appendSection(sec);
+}
+
+/*!
+ Removes all sections from the editor.
+*/
+
+void Q3DateTimeEditor::clearSections()
+{
+ d->clearSections();
+}
+
+/*!
+ Sets the selection of \a sec to start at \a selstart and end at \a
+ selend.
+*/
+
+void Q3DateTimeEditor::setSectionSelection(int sec, int selstart, int selend)
+{
+ d->setSectionSelection(sec, selstart, selend);
+}
+
+/*!
+ Sets the separator for all numbered sections to \a s. Note that
+ currently, only the first character of \a s is used.
+*/
+
+void Q3DateTimeEditor::setSeparator(const QString& s)
+{
+ d->setSeparator(s);
+ update();
+}
+
+
+/*!
+ Returns the editor's separator.
+*/
+
+QString Q3DateTimeEditor::separator() const
+{
+ return d->separator();
+}
+
+/*!
+ Returns the number of the section that has focus.
+*/
+
+int Q3DateTimeEditor::focusSection() const
+{
+ return d->focusSection();
+}
+
+
+/*!
+ Sets the focus to section \a sec. If \a sec does not exist,
+ nothing happens.
+*/
+
+bool Q3DateTimeEditor::setFocusSection(int sec)
+{
+ return d->setFocusSection(sec);
+}
+
+/*!
+ \class Q3DateTimeEditBase
+ \brief The Q3DateTimeEditBase class provides an abstraction for date and edit editors.
+
+ \compat
+
+ Small abstract class that provides some functions that are common
+ for both Q3DateEdit and Q3TimeEdit. It is used internally by
+ Q3DateTimeEditor.
+*/
+
+/*!
+ \fn Q3DateTimeEditBase::Q3DateTimeEditBase(QWidget *, const char*)
+ \internal
+*/
+
+/*!
+ \fn Q3DateTimeEditBase::setFocusSection(int)
+ \internal
+*/
+
+/*! \fn QString Q3DateTimeEditBase::sectionFormattedText(int sec)
+ \internal
+
+ Pure virtual function which returns the formatted text of section \a
+ sec.
+
+*/
+
+/*! \fn void Q3DateTimeEditBase::stepUp()
+ \internal
+
+ Pure virtual slot which is called whenever the user increases the
+ number in a section by pressing the widget's arrow buttons or the
+ keyboard's arrow keys.
+*/
+
+/*! \fn void Q3DateTimeEditBase::stepDown()
+ \internal
+
+ Pure virtual slot which is called whenever the user decreases the
+ number in a section by pressing the widget's arrow buttons or the
+ keyboard's arrow keys.
+
+*/
+
+/*! \fn void Q3DateTimeEditBase::addNumber(int sec, int num)
+ \internal
+
+ Pure virtual function which is called whenever the user types a number.
+ \a sec indicates the section where the number should be added. \a
+ num is the number that was pressed.
+*/
+
+/*! \fn void Q3DateTimeEditBase::removeLastNumber(int sec)
+ \internal
+
+ Pure virtual function which is called whenever the user tries to
+ remove the last number from \a sec by pressing the delete key.
+*/
+
+////////////////
+
+class Q3DateEditPrivate
+{
+public:
+ int y;
+ int m;
+ int d;
+ // remembers the last entry for the day.
+ // if the day is 31 and you cycle through the months,
+ // the day will be 31 again if you reach a month with 31 days
+ // otherwise it will be the highest day in the month
+ int dayCache;
+ int yearSection;
+ int monthSection;
+ int daySection;
+ Q3DateEdit::Order ord;
+ bool overwrite;
+ bool adv;
+ int timerId;
+ bool typing;
+ QDate min;
+ QDate max;
+ bool changed;
+ Q3DateTimeEditor *ed;
+ Q3SpinWidget *controls;
+};
+
+
+/*!
+ \class Q3DateEdit
+ \brief The Q3DateEdit class provides a date editor.
+
+ \compat
+
+ Q3DateEdit allows the user to edit dates by using the keyboard or
+ the arrow keys to increase/decrease date values. The arrow keys
+ can be used to move from section to section within the Q3DateEdit
+ box. Dates appear in accordance with the local date/time settings
+ or in year, month, day order if the system doesn't provide this
+ information. It is recommended that the Q3DateEdit be initialised
+ with a date, e.g.
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3datetimeedit.cpp 0
+
+ Here we've created a new Q3DateEdit object initialised with today's
+ date and restricted the valid date range to today plus or minus
+ 365 days. We've set the order to month, day, year. If the auto
+ advance property is true (as we've set it here) when the user
+ completes a section of the date, e.g. enters two digits for the
+ month, they are automatically taken to the next section.
+
+ The maximum and minimum values for a date value in the date editor
+ default to the maximum and minimum values for a QDate. You can
+ change this by calling setMinValue(), setMaxValue() or setRange().
+
+ Terminology: A Q3DateEdit widget comprises three 'sections', one
+ each for the year, month and day. You can change the separator
+ character using Q3DateTimeEditor::setSeparator(), by default the
+ separator will be taken from the systems settings. If that is
+ not possible, it defaults to "-".
+
+ \img datetimewidgets.png Date Time Widgets
+
+ \sa QDate Q3TimeEdit Q3DateTimeEdit
+*/
+
+/*!
+ \enum Q3DateEdit::Order
+
+ This enum defines the order in which the sections that comprise a
+ date appear.
+
+ \value MDY month-day-year
+ \value DMY day-month-year
+ \value YMD year-month-day (the default)
+ \omitvalue YDM
+*/
+
+/*!
+ \enum Q3TimeEdit::Display
+
+ This enum defines the sections that comprise a time
+
+ \value Hours The hours section
+ \value Minutes The minutes section
+ \value Seconds The seconds section
+ \value AMPM The AM/PM section
+
+ The values can be or'ed together to show any combination.
+*/
+
+/*!
+ Constructs an empty date editor which is a child of \a parent and
+ called name \a name.
+*/
+
+Q3DateEdit::Q3DateEdit(QWidget * parent, const char * name)
+ : Q3DateTimeEditBase(parent, name)
+{
+ init();
+ updateButtons();
+}
+
+/*!
+ \overload
+
+ Constructs a date editor with the initial value \a date, parent \a
+ parent and called \a name.
+
+ The date editor is initialized with \a date.
+*/
+
+Q3DateEdit::Q3DateEdit(const QDate& date, QWidget * parent, const char * name)
+ : Q3DateTimeEditBase(parent, name)
+{
+ init();
+ setDate(date);
+}
+
+/*! \internal
+*/
+void Q3DateEdit::init()
+{
+ d = new Q3DateEditPrivate();
+ d->controls = new QDateTimeSpinWidget(this, 0);
+ d->ed = new Q3DateTimeEditor(this, d->controls);
+ d->controls->setEditWidget(d->ed);
+ setFocusProxy(d->ed);
+ connect(d->controls, SIGNAL(stepUpPressed()), SLOT(stepUp()));
+ connect(d->controls, SIGNAL(stepDownPressed()), SLOT(stepDown()));
+ connect(this, SIGNAL(valueChanged(QDate)), SLOT(updateButtons()));
+ d->ed->appendSection(QNumberSection(0,4));
+ d->ed->appendSection(QNumberSection(5,7));
+ d->ed->appendSection(QNumberSection(8,10));
+
+ d->yearSection = -1;
+ d->monthSection = -1;
+ d->daySection = -1;
+
+ d->y = 0;
+ d->m = 0;
+ d->d = 0;
+ d->dayCache = 0;
+ setOrder(localOrder());
+ setFocusSection(0);
+ d->overwrite = true;
+ d->adv = false;
+ d->timerId = 0;
+ d->typing = false;
+ d->min = QDate(1752, 9, 14);
+ d->max = QDate(8000, 12, 31);
+ d->changed = false;
+
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ refcount++;
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3DateEdit::~Q3DateEdit()
+{
+ delete d;
+ if (!--refcount)
+ cleanup();
+}
+
+/*!
+ \property Q3DateEdit::minValue
+
+ \brief the editor's minimum value
+
+ Setting the minimum date value is equivalent to calling
+ Q3DateEdit::setRange(\e d, maxValue()), where \e d is the minimum
+ date. The default minimum date is 1752-09-14.
+
+ \sa maxValue setRange()
+*/
+
+QDate Q3DateEdit::minValue() const
+{
+ return d->min;
+}
+
+/*!
+ \property Q3DateEdit::maxValue
+
+ \brief the editor's maximum value
+
+ Setting the maximum date value for the editor is equivalent to
+ calling Q3DateEdit::setRange(minValue(), \e d), where \e d is the
+ maximum date. The default maximum date is 8000-12-31.
+
+ \sa minValue setRange()
+*/
+
+QDate Q3DateEdit::maxValue() const
+{
+ return d->max;
+}
+
+
+/*!
+ Sets the valid input range for the editor to be from \a min to \a
+ max inclusive. If \a min is invalid no minimum date will be set.
+ Similarly, if \a max is invalid no maximum date will be set.
+*/
+
+void Q3DateEdit::setRange(const QDate& min, const QDate& max)
+{
+ if (min.isValid())
+ d->min = min;
+ if (max.isValid())
+ d->max = max;
+}
+
+/*!
+ Sets the separator to \a s. Note that currently only the first
+ character of \a s is used.
+*/
+
+void Q3DateEdit::setSeparator(const QString& s)
+{
+ d->ed->setSeparator(s);
+}
+
+/*!
+ Returns the editor's separator.
+*/
+
+QString Q3DateEdit::separator() const
+{
+ return d->ed->separator();
+}
+
+
+/*!
+ Enables/disables the push buttons according to the min/max date
+ for this widget.
+*/
+
+void Q3DateEdit::updateButtons()
+{
+ if (!isEnabled())
+ return;
+
+ bool upEnabled = date() < maxValue();
+ bool downEnabled = date() > minValue();
+
+ d->controls->setUpEnabled(upEnabled);
+ d->controls->setDownEnabled(downEnabled);
+}
+
+/*! \reimp
+ */
+void Q3DateEdit::resizeEvent(QResizeEvent *)
+{
+ d->controls->resize(width(), height());
+}
+
+/*! \reimp
+
+*/
+QSize Q3DateEdit::sizeHint() const
+{
+ ensurePolished();
+ QFontMetrics fm(font());
+ int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this);
+ int h = qMax(fm.lineSpacing(), 14) + 2;
+ int w = 2 + fm.width(QLatin1Char('9')) * 8 + fm.width(d->ed->separator()) * 2 + d->controls->upRect().width() + fw * 4;
+
+ return QSize(w, qMax(h + fw * 2,20)).expandedTo(QApplication::globalStrut());
+}
+
+/*! \reimp
+
+*/
+QSize Q3DateEdit::minimumSizeHint() const
+{
+ return sizeHint();
+}
+
+
+/*!
+ Returns the formatted number for section \a sec. This will
+ correspond to either the year, month or day section, depending on
+ the current display order.
+
+ \sa setOrder()
+*/
+
+QString Q3DateEdit::sectionFormattedText(int sec)
+{
+ QString txt;
+ txt = sectionText(sec);
+ if (d->typing && sec == d->ed->focusSection())
+ d->ed->setSectionSelection(sec, sectionOffsetEnd(sec) - txt.length(),
+ sectionOffsetEnd(sec));
+ else
+ d->ed->setSectionSelection(sec, sectionOffsetEnd(sec) - sectionLength(sec),
+ sectionOffsetEnd(sec));
+ txt = txt.rightJustified(sectionLength(sec), QDATETIMEEDIT_HIDDEN_CHAR);
+ return txt;
+}
+
+
+/*!
+ Returns the desired length (number of digits) of section \a sec.
+ This will correspond to either the year, month or day section,
+ depending on the current display order.
+
+ \sa setOrder()
+*/
+
+int Q3DateEdit::sectionLength(int sec) const
+{
+ int val = 0;
+ if (sec == d->yearSection) {
+ val = 4;
+ } else if (sec == d->monthSection) {
+ val = 2;
+ } else if (sec == d->daySection) {
+ val = 2;
+ }
+ return val;
+}
+
+/*!
+ Returns the text of section \a sec. This will correspond to either
+ the year, month or day section, depending on the current display
+ order.
+
+ \sa setOrder()
+*/
+
+QString Q3DateEdit::sectionText(int sec) const
+{
+ int val = 0;
+ if (sec == d->yearSection) {
+ val = d->y;
+ } else if (sec == d->monthSection) {
+ val = d->m;
+ } else if (sec == d->daySection) {
+ val = d->d;
+ }
+ return QString::number(val);
+}
+
+/*! \internal
+
+ Returns the end of the section offset \a sec.
+
+*/
+
+int Q3DateEdit::sectionOffsetEnd(int sec) const
+{
+ if (sec == d->yearSection) {
+ switch(d->ord) {
+ case DMY:
+ case MDY:
+ return sectionOffsetEnd(sec-1) + separator().length() + sectionLength(sec);
+ case YMD:
+ case YDM:
+ return sectionLength(sec);
+ }
+ } else if (sec == d->monthSection) {
+ switch(d->ord) {
+ case DMY:
+ case YDM:
+ case YMD:
+ return sectionOffsetEnd(sec-1) + separator().length() + sectionLength(sec);
+ case MDY:
+ return sectionLength(sec);
+ }
+ } else if (sec == d->daySection) {
+ switch(d->ord) {
+ case DMY:
+ return sectionLength(sec);
+ case YMD:
+ case MDY:
+ case YDM:
+ return sectionOffsetEnd(sec-1) + separator().length() + sectionLength(sec);
+ }
+ }
+ return 0;
+}
+
+
+/*!
+ \property Q3DateEdit::order
+ \brief the order in which the year, month and day appear
+
+ The default order is locale dependent.
+
+ \sa Order
+*/
+
+void Q3DateEdit::setOrder(Q3DateEdit::Order order)
+{
+ d->ord = order;
+ switch(d->ord) {
+ case DMY:
+ d->yearSection = 2;
+ d->monthSection = 1;
+ d->daySection = 0;
+ break;
+ case MDY:
+ d->yearSection = 2;
+ d->monthSection = 0;
+ d->daySection = 1;
+ break;
+ case YMD:
+ d->yearSection = 0;
+ d->monthSection = 1;
+ d->daySection = 2;
+ break;
+ case YDM:
+ d->yearSection = 0;
+ d->monthSection = 2;
+ d->daySection = 1;
+ break;
+ }
+ if (isVisible())
+ d->ed->repaint(d->ed->rect());
+}
+
+
+Q3DateEdit::Order Q3DateEdit::order() const
+{
+ return d->ord;
+}
+
+
+/*! \internal
+
+*/
+void Q3DateEdit::stepUp()
+{
+ int sec = d->ed->focusSection();
+ bool accepted = false;
+ if (sec == d->yearSection) {
+ if (!outOfRange(d->y+1, d->m, d->d)) {
+ accepted = true;
+ setYear(d->y+1);
+ }
+ } else if (sec == d->monthSection) {
+ if (!outOfRange(d->y, d->m+1, d->d)) {
+ accepted = true;
+ setMonth(d->m+1);
+ }
+ } else if (sec == d->daySection) {
+ if (!outOfRange(d->y, d->m, d->d+1)) {
+ accepted = true;
+ setDay(d->d+1);
+ }
+ }
+ if (accepted) {
+ d->changed = false;
+ emit valueChanged(date());
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+
+
+/*! \internal
+
+*/
+
+void Q3DateEdit::stepDown()
+{
+ int sec = d->ed->focusSection();
+ bool accepted = false;
+ if (sec == d->yearSection) {
+ if (!outOfRange(d->y-1, d->m, d->d)) {
+ accepted = true;
+ setYear(d->y-1);
+ }
+ } else if (sec == d->monthSection) {
+ if (!outOfRange(d->y, d->m-1, d->d)) {
+ accepted = true;
+ setMonth(d->m-1);
+ }
+ } else if (sec == d->daySection) {
+ if (!outOfRange(d->y, d->m, d->d-1)) {
+ accepted = true;
+ setDay(d->d-1);
+ }
+ }
+ if (accepted) {
+ d->changed = false;
+ emit valueChanged(date());
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+/*!
+ Sets the year to \a year, which must be a valid year. The range
+ currently supported is from 1752 to 8000.
+
+ \sa QDate
+*/
+
+void Q3DateEdit::setYear(int year)
+{
+ if (year < 1752)
+ year = 1752;
+ if (year > 8000)
+ year = 8000;
+ if (!outOfRange(year, d->m, d->d)) {
+ d->y = year;
+ setMonth(d->m);
+ int tmp = d->dayCache;
+ setDay(d->dayCache);
+ d->dayCache = tmp;
+ }
+}
+
+
+/*!
+ Sets the month to \a month, which must be a valid month, i.e.
+ between 1 and 12.
+*/
+
+void Q3DateEdit::setMonth(int month)
+{
+ if (month < 1)
+ month = 1;
+ if (month > 12)
+ month = 12;
+ if (!outOfRange(d->y, month, d->d)) {
+ d->m = month;
+ int tmp = d->dayCache;
+ setDay(d->dayCache);
+ d->dayCache = tmp;
+ }
+}
+
+
+/*!
+ Sets the day to \a day, which must be a valid day. The function
+ will ensure that the \a day set is valid for the month and year.
+*/
+
+void Q3DateEdit::setDay(int day)
+{
+ if (day < 1)
+ day = 1;
+ if (day > 31)
+ day = 31;
+ if (d->m > 0 && d->y > 1752) {
+ while (!QDate::isValid(d->y, d->m, day))
+ --day;
+ if (!outOfRange(d->y, d->m, day))
+ d->d = day;
+ } else if (d->m > 0) {
+ if (day > 0 && day < 32) {
+ if (!outOfRange(d->y, d->m, day))
+ d->d = day;
+ }
+ }
+ d->dayCache = d->d;
+}
+
+
+/*!
+ \property Q3DateEdit::date
+ \brief the editor's date value.
+
+ If the date property is not valid, the editor displays all zeroes
+ and Q3DateEdit::date() will return an invalid date. It is strongly
+ recommended that the editor is given a default date value (e.g.
+ currentDate()). That way, attempts to set the date property to an
+ invalid date will fail.
+
+ When changing the date property, if the date is less than
+ minValue(), or is greater than maxValue(), nothing happens.
+*/
+
+void Q3DateEdit::setDate(const QDate& date)
+{
+ if (!date.isValid()) {
+ d->y = 0;
+ d->m = 0;
+ d->d = 0;
+ d->dayCache = 0;
+ } else {
+ if (date > maxValue() || date < minValue())
+ return;
+ d->y = date.year();
+ d->m = date.month();
+ d->d = date.day();
+ d->dayCache = d->d;
+ emit valueChanged(date);
+ }
+ d->changed = false;
+ d->ed->repaint(d->ed->rect());
+}
+
+QDate Q3DateEdit::date() const
+{
+ if (QDate::isValid(d->y, d->m, d->d))
+ return QDate(d->y, d->m, d->d);
+ return QDate();
+}
+
+/*! \internal
+
+ Returns true if \a y, \a m, \a d is out of range, otherwise returns
+ false.
+
+ \sa setRange()
+
+*/
+
+bool Q3DateEdit::outOfRange(int y, int m, int d) const
+{
+ if (QDate::isValid(y, m, d)) {
+ QDate currentDate(y, m, d);
+ if (currentDate > maxValue() ||
+ currentDate < minValue()) {
+ //## outOfRange should set overwrite?
+ return true;
+ }
+ return false;
+ }
+ return false; /* assume ok */
+}
+
+/*! \internal
+
+*/
+
+void Q3DateEdit::addNumber(int sec, int num)
+{
+ if (sec == -1)
+ return;
+ if (d->timerId)
+ killTimer(d->timerId);
+ d->timerId = 0;
+ bool overwrite = false;
+ bool accepted = false;
+ d->typing = true;
+ QString txt;
+ if (sec == d->yearSection) {
+ txt = QString::number(d->y);
+ if (d->overwrite || txt.length() == 4) {
+ accepted = true;
+ d->y = num;
+ } else {
+ txt += QString::number(num);
+ if (txt.length() == 4 ) {
+ const int val = qBound(1792, txt.toInt(), 8000);
+ if (outOfRange(val, d->m, d->d)) {
+ txt = QString::number(d->y);
+ } else {
+ accepted = true;
+ d->y = val;
+ }
+ } else {
+ accepted = true;
+ d->y = txt.toInt();
+ }
+ if (d->adv && txt.length() == 4) {
+ d->ed->setFocusSection(d->ed->focusSection()+1);
+ overwrite = true;
+ }
+ }
+ } else if (sec == d->monthSection) {
+ txt = QString::number(d->m);
+ if (d->overwrite || txt.length() == 2) {
+ accepted = true;
+ d->m = num;
+ } else {
+ txt += QString::number(num);
+ int temp = txt.toInt();
+ if (temp > 12)
+ temp = num;
+ if (outOfRange(d->y, temp, d->d))
+ txt = QString::number(d->m);
+ else {
+ accepted = true;
+ d->m = temp;
+ }
+ if (d->adv && txt.length() == 2) {
+ d->ed->setFocusSection(d->ed->focusSection()+1);
+ overwrite = true;
+ }
+ }
+ } else if (sec == d->daySection) {
+ txt = QString::number(d->d);
+ if (d->overwrite || txt.length() == 2) {
+ accepted = true;
+ d->d = num;
+ d->dayCache = d->d;
+ } else {
+ txt += QString::number(num);
+ int temp = txt.toInt();
+ if (temp > 31)
+ temp = num;
+ if (outOfRange(d->y, d->m, temp))
+ txt = QString::number(d->d);
+ else {
+ accepted = true;
+ d->d = temp;
+ d->dayCache = d->d;
+ }
+ if (d->adv && txt.length() == 2) {
+ d->ed->setFocusSection(d->ed->focusSection()+1);
+ overwrite = true;
+ }
+ }
+ }
+ if (accepted) {
+ d->changed = false;
+ emit valueChanged(date());
+ }
+ d->overwrite = overwrite;
+ d->timerId = startTimer(qApp->doubleClickInterval()*4);
+ d->ed->repaint(d->ed->rect());
+}
+
+
+/*! \internal
+
+*/
+
+bool Q3DateEdit::setFocusSection(int s)
+{
+ if (s != d->ed->focusSection()) {
+ if (d->timerId)
+ killTimer(d->timerId);
+ d->timerId = 0;
+ d->overwrite = true;
+ d->typing = false;
+ fix(); // will emit valueChanged if necessary
+ }
+ return d->ed->setFocusSection(s);
+}
+
+
+/*!
+ Attempts to fix any invalid date entries.
+
+ The rules applied are as follows:
+
+ \list
+ \i If the year has four digits it is left unchanged.
+ \i If the year has two digits, the year will be changed to four
+ digits in the range current year - 70 to current year + 29.
+ \i If the year has three digits in the range 100..999, the
+ current millennium, i.e. 2000, will be added giving a year
+ in the range 2100..2999.
+ \i If the day or month is 0 then it will be set to 1 or the
+ minimum valid day/month in the range.
+ \endlist
+*/
+
+void Q3DateEdit::fix()
+{
+ bool changed = false;
+ int currentYear = QDate::currentDate().year();
+ int year = d->y;
+ if (year < 100) {
+ int currentCentury = currentYear / 100;
+ year += currentCentury * 100;
+ if (currentYear > year) {
+ if (currentYear > year + 70)
+ year += 100;
+ } else {
+ if (year >= currentYear + 30)
+ year -= 100;
+ }
+ changed = true;
+ } else if (year < 1000) {
+ int currentMillennium = currentYear / 10;
+ year += currentMillennium * 10;
+ changed = true;
+ } else if (d->d == 0) {
+ d->d = 1;
+ changed = true;
+ } else if (d->m == 0) {
+ d->m = 1;
+ changed = true;
+ }
+ if (outOfRange(year, d->m, d->d)) {
+ if (minValue().isValid() && date() < minValue()) {
+ d->d = minValue().day();
+ d->dayCache = d->d;
+ d->m = minValue().month();
+ d->y = minValue().year();
+ }
+ if (date() > maxValue()) {
+ d->d = maxValue().day();
+ d->dayCache = d->d;
+ d->m = maxValue().month();
+ d->y = maxValue().year();
+ }
+ changed = true;
+ } else if (changed)
+ setYear(year);
+ if (changed) {
+ emit valueChanged(date());
+ d->changed = false;
+ }
+}
+
+
+/*! \reimp
+
+*/
+
+bool Q3DateEdit::event(QEvent *e)
+{
+ if(e->type() == QEvent::FocusOut) {
+ d->typing = false;
+ fix();
+ // the following can't be done in fix() because fix() called
+ // from all over the place and it will break the old behaviour
+ if (!QDate::isValid(d->y, d->m, d->d)) {
+ d->dayCache = d->d;
+ int i = d->d;
+ for (; i > 0; i--) {
+ d->d = i;
+ if (QDate::isValid(d->y, d->m, d->d))
+ break;
+ }
+ d->changed = true;
+ }
+ if (d->changed) {
+ emit valueChanged(date());
+ d->changed = false;
+ }
+ } else if (e->type() == QEvent::LocaleChange) {
+ readLocaleSettings();
+ d->ed->setSeparator(localDateSep());
+ setOrder(localOrder());
+ }
+ return Q3DateTimeEditBase::event(e);
+}
+
+/*!
+ \internal
+
+ Function which is called whenever the user tries to
+ remove the first number from \a sec by pressing the backspace key.
+*/
+
+void Q3DateEdit::removeFirstNumber(int sec)
+{
+ if (sec == -1)
+ return;
+ QString txt;
+ if (sec == d->yearSection) {
+ txt = QString::number(d->y);
+ txt = txt.mid(1, txt.length()) + QLatin1Char('0');
+ d->y = txt.toInt();
+ } else if (sec == d->monthSection) {
+ txt = QString::number(d->m);
+ txt = txt.mid(1, txt.length()) + QLatin1Char('0');
+ d->m = txt.toInt();
+ } else if (sec == d->daySection) {
+ txt = QString::number(d->d);
+ txt = txt.mid(1, txt.length()) + QLatin1Char('0');
+ d->d = txt.toInt();
+ d->dayCache = d->d;
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+/*! \internal
+
+*/
+
+void Q3DateEdit::removeLastNumber(int sec)
+{
+ if (sec == -1)
+ return;
+ QString txt;
+ if (sec == d->yearSection) {
+ txt = QString::number(d->y);
+ txt = txt.mid(0, txt.length()-1);
+ d->y = txt.toInt();
+ } else if (sec == d->monthSection) {
+ txt = QString::number(d->m);
+ txt = txt.mid(0, txt.length()-1);
+ d->m = txt.toInt();
+ } else if (sec == d->daySection) {
+ txt = QString::number(d->d);
+ txt = txt.mid(0, txt.length()-1);
+ d->d = txt.toInt();
+ d->dayCache = d->d;
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+/*!
+ \property Q3DateEdit::autoAdvance
+ \brief whether the editor automatically advances to the next
+ section
+
+ If autoAdvance is true, the editor will automatically advance
+ focus to the next date section if a user has completed a section.
+ The default is false.
+*/
+
+void Q3DateEdit::setAutoAdvance(bool advance)
+{
+ d->adv = advance;
+}
+
+
+bool Q3DateEdit::autoAdvance() const
+{
+ return d->adv;
+}
+
+/*! \reimp
+*/
+
+void Q3DateEdit::timerEvent(QTimerEvent *)
+{
+ d->overwrite = true;
+}
+
+/*!
+ \fn void Q3DateEdit::valueChanged(const QDate& date)
+
+ This signal is emitted whenever the editor's value changes. The \a
+ date parameter is the new value.
+*/
+
+///////////
+
+class Q3TimeEditPrivate
+{
+public:
+ int h;
+ int m;
+ int s;
+ uint display;
+ bool adv;
+ bool overwrite;
+ int timerId;
+ bool typing;
+ QTime min;
+ QTime max;
+ bool changed;
+ Q3DateTimeEditor *ed;
+ Q3SpinWidget *controls;
+};
+
+/*!
+ \class Q3TimeEdit
+ \brief The Q3TimeEdit class provides a time editor.
+
+ \compat
+
+ Q3TimeEdit allows the user to edit times by using the keyboard or
+ the arrow keys to increase/decrease time values. The arrow keys
+ can be used to move from section to section within the Q3TimeEdit
+ box. The user can automatically be moved to the next section once
+ they complete a section using setAutoAdvance(). Times appear in
+ hour, minute, second order. It is recommended that the Q3TimeEdit
+ is initialised with a time, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3datetimeedit.cpp 1
+ Here we've created a Q3TimeEdit widget set to the current time.
+ We've also set the minimum value to the current time and the
+ maximum time to one hour from now.
+
+ The maximum and minimum values for a time value in the time editor
+ default to the maximum and minimum values for a QTime. You can
+ change this by calling setMinValue(), setMaxValue() or setRange().
+
+ Terminology: A QTimeWidget consists of three sections, one each
+ for the hour, minute and second. You can change the separator
+ character using setSeparator(), by default the separator is read
+ from the system's settings.
+
+ \img datetimewidgets.png Date Time Widgets
+
+ \sa QTime Q3DateEdit Q3DateTimeEdit
+*/
+
+
+/*!
+ Constructs an empty time edit with parent \a parent and called \a
+ name.
+*/
+
+Q3TimeEdit::Q3TimeEdit(QWidget * parent, const char * name)
+ : Q3DateTimeEditBase(parent, name)
+{
+ init();
+}
+
+/*!
+ \overload
+
+ Constructs a time edit with the initial time value, \a time,
+ parent \a parent and called \a name.
+*/
+
+Q3TimeEdit::Q3TimeEdit(const QTime& time, QWidget * parent, const char * name)
+ : Q3DateTimeEditBase(parent, name)
+{
+ init();
+ setTime(time);
+}
+
+/*! \internal
+ */
+
+void Q3TimeEdit::init()
+{
+ d = new Q3TimeEditPrivate();
+ d->controls = new QDateTimeSpinWidget(this, 0);
+ d->ed = new Q3DateTimeEditor(this, d->controls, "time edit base");
+ d->controls->setEditWidget(d->ed);
+ setFocusProxy(d->ed);
+ connect(d->controls, SIGNAL(stepUpPressed()), SLOT(stepUp()));
+ connect(d->controls, SIGNAL(stepDownPressed()), SLOT(stepDown()));
+
+ d->ed->appendSection(QNumberSection(0,0, true, 0));
+ d->ed->appendSection(QNumberSection(0,0, true, 1));
+ d->ed->appendSection(QNumberSection(0,0, true, 2));
+ d->ed->setSeparator(localTimeSep());
+
+ d->h = 0;
+ d->m = 0;
+ d->s = 0;
+ d->display = Hours | Minutes | Seconds;
+ if (lAMPM) {
+ d->display |= AMPM;
+ d->ed->appendSection(QNumberSection(0,0, false, 3));
+ }
+ d->adv = false;
+ d->overwrite = true;
+ d->timerId = 0;
+ d->typing = false;
+ d->min = QTime(0, 0, 0);
+ d->max = QTime(23, 59, 59);
+ d->changed = false;
+
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+ refcount++;
+}
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3TimeEdit::~Q3TimeEdit()
+{
+ delete d;
+ if (!--refcount)
+ cleanup();
+}
+
+/*!
+ \property Q3TimeEdit::minValue
+ \brief the minimum time value
+
+ Setting the minimum time value is equivalent to calling
+ Q3TimeEdit::setRange(\e t, maxValue()), where \e t is the minimum
+ time. The default minimum time is 00:00:00.
+
+ \sa maxValue setRange()
+*/
+
+QTime Q3TimeEdit::minValue() const
+{
+ return d->min;
+}
+
+/*!
+ \property Q3TimeEdit::maxValue
+ \brief the maximum time value
+
+ Setting the maximum time value is equivalent to calling
+ Q3TimeEdit::setRange(minValue(), \e t), where \e t is the maximum
+ time. The default maximum time is 23:59:59.
+
+ \sa minValue setRange()
+*/
+
+QTime Q3TimeEdit::maxValue() const
+{
+ return d->max;
+}
+
+
+/*!
+ Sets the valid input range for the editor to be from \a min to \a
+ max inclusive. If \a min is invalid no minimum time is set.
+ Similarly, if \a max is invalid no maximum time is set.
+*/
+
+void Q3TimeEdit::setRange(const QTime& min, const QTime& max)
+{
+ if (min.isValid())
+ d->min = min;
+ if (max.isValid())
+ d->max = max;
+}
+
+/*!
+ \property Q3TimeEdit::display
+ \brief the sections that are displayed in the time edit
+
+ The value can be any combination of the values in the Display enum.
+ By default, the widget displays hours, minutes and seconds.
+*/
+void Q3TimeEdit::setDisplay(uint display)
+{
+ if (d->display == display)
+ return;
+
+ d->ed->clearSections();
+ d->display = display;
+ if (d->display & Hours)
+ d->ed->appendSection(QNumberSection(0,0, true, 0));
+ if (d->display & Minutes)
+ d->ed->appendSection(QNumberSection(0,0, true, 1));
+ if (d->display & Seconds)
+ d->ed->appendSection(QNumberSection(0,0, true, 2));
+ if (d->display & AMPM)
+ d->ed->appendSection(QNumberSection(0,0, false, 3));
+
+ d->ed->setFocusSection(0);
+ d->ed->update();
+}
+
+uint Q3TimeEdit::display() const
+{
+ return d->display;
+}
+
+/*!
+ \property Q3TimeEdit::time
+ \brief the editor's time value.
+
+ When changing the time property, if the time is less than
+ minValue(), or is greater than maxValue(), nothing happens.
+*/
+
+void Q3TimeEdit::setTime(const QTime& time)
+{
+ if (!time.isValid()) {
+ d->h = 0;
+ d->m = 0;
+ d->s = 0;
+ } else {
+ if (time > maxValue() || time < minValue())
+ return;
+ d->h = time.hour();
+ d->m = time.minute();
+ d->s = time.second();
+ emit valueChanged(time);
+ }
+ d->changed = false;
+ d->ed->repaint(d->ed->rect());
+}
+
+QTime Q3TimeEdit::time() const
+{
+ if (QTime::isValid(d->h, d->m, d->s))
+ return QTime(d->h, d->m, d->s);
+ return QTime();
+}
+
+/*!
+ \property Q3TimeEdit::autoAdvance
+ \brief whether the editor automatically advances to the next
+ section
+
+ If autoAdvance is true, the editor will automatically advance
+ focus to the next time section if a user has completed a section.
+ The default is false.
+*/
+
+void Q3TimeEdit::setAutoAdvance(bool advance)
+{
+ d->adv = advance;
+}
+
+bool Q3TimeEdit::autoAdvance() const
+{
+ return d->adv;
+}
+
+/*!
+ Sets the separator to \a s. Note that currently only the first
+ character of \a s is used.
+*/
+
+void Q3TimeEdit::setSeparator(const QString& s)
+{
+ d->ed->setSeparator(s);
+}
+
+/*!
+ Returns the editor's separator.
+*/
+
+QString Q3TimeEdit::separator() const
+{
+ return d->ed->separator();
+}
+
+
+/*!
+ \fn void Q3TimeEdit::valueChanged(const QTime& time)
+
+ This signal is emitted whenever the editor's value changes. The \a
+ time parameter is the new value.
+*/
+
+/*! \reimp
+
+*/
+
+bool Q3TimeEdit::event(QEvent *e)
+{
+ if (e->type() == QEvent::FocusOut) {
+ d->typing = false;
+ if (d->changed) {
+ emit valueChanged(time());
+ d->changed = false;
+ }
+ } else if (e->type() == QEvent::LocaleChange) {
+ readLocaleSettings();
+ d->ed->setSeparator(localTimeSep());
+ }
+ return Q3DateTimeEditBase::event(e);
+}
+
+/*! \reimp
+
+*/
+
+void Q3TimeEdit::timerEvent(QTimerEvent *)
+{
+ d->overwrite = true;
+}
+
+
+/*! \internal
+
+*/
+
+void Q3TimeEdit::stepUp()
+{
+ int sec = d->ed->mapSection(d->ed->focusSection());
+ bool accepted = true;
+ switch(sec) {
+ case 0:
+ if (!outOfRange(d->h+1, d->m, d->s))
+ setHour(d->h+1);
+ else
+ setHour(d->min.hour());
+ break;
+ case 1:
+ if (!outOfRange(d->h, d->m+1, d->s))
+ setMinute(d->m+1);
+ else
+ setMinute(d->min.minute());
+ break;
+ case 2:
+ if (!outOfRange(d->h, d->m, d->s+1))
+ setSecond(d->s+1);
+ else
+ setSecond(d->min.second());
+ break;
+ case 3:
+ if (d->h < 12)
+ setHour(d->h+12);
+ else
+ setHour(d->h-12);
+ break;
+ default:
+ accepted = false;
+ qWarning("Q3TimeEdit::stepUp: Focus section out of range!");
+ break;
+ }
+ if (accepted) {
+ d->changed = false;
+ emit valueChanged(time());
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+
+/*! \internal
+
+*/
+
+void Q3TimeEdit::stepDown()
+{
+ int sec = d->ed->mapSection(d->ed->focusSection());
+
+ bool accepted = true;
+ switch(sec) {
+ case 0:
+ if (!outOfRange(d->h-1, d->m, d->s))
+ setHour(d->h-1);
+ else
+ setHour(d->max.hour());
+ break;
+ case 1:
+ if (!outOfRange(d->h, d->m-1, d->s))
+ setMinute(d->m-1);
+ else
+ setMinute(d->max.minute());
+ break;
+ case 2:
+ if (!outOfRange(d->h, d->m, d->s-1))
+ setSecond(d->s-1);
+ else
+ setSecond(d->max.second());
+ break;
+ case 3:
+ if (d->h > 11)
+ setHour(d->h-12);
+ else
+ setHour(d->h+12);
+ break;
+ default:
+ accepted = false;
+ qWarning("Q3TimeEdit::stepDown: Focus section out of range!");
+ break;
+ }
+ if (accepted) {
+ d->changed = false;
+ emit valueChanged(time());
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+
+/*!
+ Returns the formatted number for section \a sec. This will
+ correspond to either the hour, minute or second section, depending
+ on \a sec.
+*/
+
+QString Q3TimeEdit::sectionFormattedText(int sec)
+{
+ QString txt;
+ txt = sectionText(sec);
+ txt = txt.rightJustified(2, QDATETIMEEDIT_HIDDEN_CHAR);
+ int offset = sec*2+sec*separator().length() + txt.length();
+ if (d->typing && sec == d->ed->focusSection())
+ d->ed->setSectionSelection(sec, offset - txt.length(), offset);
+ else
+ d->ed->setSectionSelection(sec, offset - txt.length(), offset);
+
+ return txt;
+}
+
+
+/*! \internal
+
+*/
+
+bool Q3TimeEdit::setFocusSection(int sec)
+{
+ if (sec != d->ed->focusSection()) {
+ if (d->timerId)
+ killTimer(d->timerId);
+ d->timerId = 0;
+ d->overwrite = true;
+ d->typing = false;
+ QString txt = sectionText(sec);
+ txt = txt.rightJustified(2, QDATETIMEEDIT_HIDDEN_CHAR);
+ int offset = sec*2+sec*separator().length() + txt.length();
+ d->ed->setSectionSelection(sec, offset - txt.length(), offset);
+ if (d->changed) {
+ emit valueChanged(time());
+ d->changed = false;
+ }
+ }
+ return d->ed->setFocusSection(sec);
+}
+
+
+/*!
+ Sets the hour to \a h, which must be a valid hour, i.e. in the
+ range 0..24.
+*/
+
+void Q3TimeEdit::setHour(int h)
+{
+ if (h < 0)
+ h = 0;
+ if (h > 23)
+ h = 23;
+ d->h = h;
+}
+
+
+/*!
+ Sets the minute to \a m, which must be a valid minute, i.e. in the
+ range 0..59.
+*/
+
+void Q3TimeEdit::setMinute(int m)
+{
+ if (m < 0)
+ m = 0;
+ if (m > 59)
+ m = 59;
+ d->m = m;
+}
+
+
+/*!
+ Sets the second to \a s, which must be a valid second, i.e. in the
+ range 0..59.
+*/
+
+void Q3TimeEdit::setSecond(int s)
+{
+ if (s < 0)
+ s = 0;
+ if (s > 59)
+ s = 59;
+ d->s = s;
+}
+
+
+/*! \internal
+
+ Returns the text of section \a sec.
+
+*/
+
+QString Q3TimeEdit::sectionText(int sec)
+{
+ sec = d->ed->mapSection(sec);
+
+ QString txt;
+ switch(sec) {
+ case 0:
+ if (!(d->display & AMPM) || (d->h < 13 && d->h)) { // I wished the day stared at 0:00 for everybody
+ txt = QString::number(d->h);
+ } else {
+ if (d->h)
+ txt = QString::number(d->h - 12);
+ else
+ txt = QLatin1String("12");
+ }
+ break;
+ case 1:
+ txt = QString::number(d->m);
+ break;
+ case 2:
+ txt = QString::number(d->s);
+ break;
+ case 3:
+ if (d->h < 12) {
+ if (lAM)
+ txt = *lAM;
+ else
+ txt = QString::fromLatin1("AM");
+ } else {
+ if (lPM)
+ txt = *lPM;
+ else
+ txt = QString::fromLatin1("PM");
+ }
+ break;
+ default:
+ break;
+ }
+ return txt;
+}
+
+
+/*! \internal
+ Returns true if \a h, \a m, and \a s are out of range.
+ */
+
+bool Q3TimeEdit::outOfRange(int h, int m, int s) const
+{
+ if (QTime::isValid(h, m, s)) {
+ QTime currentTime(h, m, s);
+ if (currentTime > maxValue() ||
+ currentTime < minValue())
+ return true;
+ else
+ return false;
+ }
+ return true;
+}
+
+/*! \internal
+
+*/
+
+void Q3TimeEdit::addNumber(int sec, int num)
+{
+ if (sec == -1)
+ return;
+ sec = d->ed->mapSection(sec);
+ if (d->timerId)
+ killTimer(d->timerId);
+ d->timerId = 0;
+ bool overwrite = false;
+ bool accepted = false;
+ d->typing = true;
+ QString txt;
+
+ switch(sec) {
+ case 0:
+ txt = (d->display & AMPM && d->h > 12) ?
+ QString::number(d->h - 12) : QString::number(d->h);
+
+ if (d->overwrite || txt.length() == 2) {
+ if (d->display & AMPM && num == 0)
+ break; // Don't process 0 in 12 hour clock mode
+ if (d->display & AMPM && d->h > 11)
+ num += 12;
+ if (!outOfRange(num, d->m, d->s)) {
+ accepted = true;
+ d->h = num;
+ }
+ } else {
+ txt += QString::number(num);
+ int temp = txt.toInt();
+
+ if (d->display & AMPM) {
+ if (temp == 12) {
+ if (d->h < 12) {
+ temp = 0;
+ }
+ accepted = true;
+ } else if (outOfRange(temp + 12, d->m, d->s)) {
+ txt = QString::number(d->h);
+ } else {
+ if (d->h > 11) {
+ temp += 12;
+ }
+ accepted = true;
+ }
+ } else if (!(d->display & AMPM) && outOfRange(temp, d->m, d->s)) {
+ txt = QString::number(d->h);
+ } else {
+ accepted = true;
+ }
+
+ if (accepted)
+ d->h = temp;
+
+ if (d->adv && txt.length() == 2) {
+ setFocusSection(d->ed->focusSection()+1);
+ overwrite = true;
+ }
+ }
+ break;
+
+ case 1:
+ txt = QString::number(d->m);
+ if (d->overwrite || txt.length() == 2) {
+ if (!outOfRange(d->h, num, d->s)) {
+ accepted = true;
+ d->m = num;
+ }
+ } else {
+ txt += QString::number(num);
+ int temp = txt.toInt();
+ if (temp > 59)
+ temp = num;
+ if (outOfRange(d->h, temp, d->s))
+ txt = QString::number(d->m);
+ else {
+ accepted = true;
+ d->m = temp;
+ }
+ if (d->adv && txt.length() == 2) {
+ setFocusSection(d->ed->focusSection()+1);
+ overwrite = true;
+ }
+ }
+ break;
+
+ case 2:
+ txt = QString::number(d->s);
+ if (d->overwrite || txt.length() == 2) {
+ if (!outOfRange(d->h, d->m, num)) {
+ accepted = true;
+ d->s = num;
+ }
+ } else {
+ txt += QString::number(num);
+ int temp = txt.toInt();
+ if (temp > 59)
+ temp = num;
+ if (outOfRange(d->h, d->m, temp))
+ txt = QString::number(d->s);
+ else {
+ accepted = true;
+ d->s = temp;
+ }
+ if (d->adv && txt.length() == 2) {
+ setFocusSection(d->ed->focusSection()+1);
+ overwrite = true;
+ }
+ }
+ break;
+
+ case 3:
+ break;
+
+ default:
+ break;
+ }
+ d->changed = !accepted;
+ if (accepted)
+ emit valueChanged(time());
+ d->overwrite = overwrite;
+ d->timerId = startTimer(qApp->doubleClickInterval()*4);
+ d->ed->repaint(d->ed->rect());
+}
+
+
+/*!
+ \internal
+
+ Function which is called whenever the user tries to
+ remove the first number from \a sec by pressing the backspace key.
+*/
+
+void Q3TimeEdit::removeFirstNumber(int sec)
+{
+ if (sec == -1)
+ return;
+ sec = d->ed->mapSection(sec);
+ QString txt;
+ switch(sec) {
+ case 0:
+ txt = QString::number(d->h);
+ break;
+ case 1:
+ txt = QString::number(d->m);
+ break;
+ case 2:
+ txt = QString::number(d->s);
+ break;
+ }
+ txt = txt.mid(1, txt.length()) + QLatin1Char('0');
+ switch(sec) {
+ case 0:
+ d->h = txt.toInt();
+ break;
+ case 1:
+ d->m = txt.toInt();
+ break;
+ case 2:
+ d->s = txt.toInt();
+ break;
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+/*! \internal
+
+*/
+void Q3TimeEdit::removeLastNumber(int sec)
+{
+ if (sec == -1)
+ return;
+ sec = d->ed->mapSection(sec);
+ QString txt;
+ switch(sec) {
+ case 0:
+ txt = QString::number(d->h);
+ break;
+ case 1:
+ txt = QString::number(d->m);
+ break;
+ case 2:
+ txt = QString::number(d->s);
+ break;
+ }
+ txt = txt.mid(0, txt.length()-1);
+ switch(sec) {
+ case 0:
+ d->h = txt.toInt();
+ break;
+ case 1:
+ d->m = txt.toInt();
+ break;
+ case 2:
+ d->s = txt.toInt();
+ break;
+ }
+ d->ed->repaint(d->ed->rect());
+}
+
+/*! \reimp
+ */
+void Q3TimeEdit::resizeEvent(QResizeEvent *)
+{
+ d->controls->resize(width(), height());
+}
+
+/*! \reimp
+*/
+QSize Q3TimeEdit::sizeHint() const
+{
+ ensurePolished();
+ QFontMetrics fm(font());
+ int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this);
+ int h = fm.lineSpacing() + 2;
+ int w = 2 + fm.width(QLatin1Char('9')) * 6 + fm.width(d->ed->separator()) * 2 +
+ d->controls->upRect().width() + fw * 4;
+ if (d->display & AMPM) {
+ if (lAM)
+ w += fm.width(*lAM) + 4;
+ else
+ w += fm.width(QString::fromLatin1("AM")) + 4;
+ }
+
+ return QSize(w, qMax(h + fw * 2,20)).expandedTo(QApplication::globalStrut());
+}
+
+/*! \reimp
+*/
+QSize Q3TimeEdit::minimumSizeHint() const
+{
+ return sizeHint();
+}
+
+/*!
+ \internal
+ Enables/disables the push buttons according to the min/max time
+ for this widget.
+*/
+
+void Q3TimeEdit::updateButtons()
+{
+ if (!isEnabled())
+ return;
+
+ bool upEnabled = time() < maxValue();
+ bool downEnabled = time() > minValue();
+
+ d->controls->setUpEnabled(upEnabled);
+ d->controls->setDownEnabled(downEnabled);
+}
+
+
+class Q3DateTimeEditPrivate
+{
+public:
+ bool adv;
+};
+
+/*!
+ \class Q3DateTimeEdit
+ \brief The Q3DateTimeEdit class combines a Q3DateEdit and Q3TimeEdit
+ widget into a single widget for editing datetimes.
+
+ \compat
+
+ Q3DateTimeEdit consists of a Q3DateEdit and Q3TimeEdit widget placed
+ side by side and offers the functionality of both. The user can
+ edit the date and time by using the keyboard or the arrow keys to
+ increase/decrease date or time values. The Tab key can be used to
+ move from section to section within the Q3DateTimeEdit widget, and
+ the user can be moved automatically when they complete a section
+ using setAutoAdvance(). The datetime can be set with
+ setDateTime().
+
+ The date format is read from the system's locale settings. It is
+ set to year, month, day order if that is not possible. See
+ Q3DateEdit::setOrder() to change this. Times appear in the order
+ hours, minutes, seconds using the 24 hour clock.
+
+ It is recommended that the Q3DateTimeEdit is initialised with a
+ datetime, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3datetimeedit.cpp 2
+ Here we've created a new Q3DateTimeEdit set to the current date and
+ time, and set the date to have a minimum date of now and a maximum
+ date of a week from now.
+
+ Terminology: A Q3DateEdit widget consists of three 'sections', one
+ each for the year, month and day. Similarly a Q3TimeEdit consists
+ of three sections, one each for the hour, minute and second. The
+ character that separates each date section is specified with
+ setDateSeparator(); similarly setTimeSeparator() is used for the
+ time sections.
+
+ \img datetimewidgets.png Date Time Widgets
+
+ \sa Q3DateEdit Q3TimeEdit
+*/
+
+/*!
+ Constructs an empty datetime edit with parent \a parent and called
+ \a name.
+*/
+Q3DateTimeEdit::Q3DateTimeEdit(QWidget * parent, const char * name)
+ : QWidget(parent, name)
+{
+ init();
+}
+
+
+/*!
+ \overload
+
+ Constructs a datetime edit with the initial value \a datetime,
+ parent \a parent and called \a name.
+*/
+Q3DateTimeEdit::Q3DateTimeEdit(const QDateTime& datetime,
+ QWidget * parent, const char * name)
+ : QWidget(parent, name)
+{
+ init();
+ setDateTime(datetime);
+}
+
+
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3DateTimeEdit::~Q3DateTimeEdit()
+{
+ delete d;
+}
+
+
+/*! \fn void Q3DateTimeEdit::resizeEvent(QResizeEvent *event)
+ \reimp
+
+ Intercepts and handles the resize \a event, which hase a
+ special meaning for the Q3DateTimeEdit.
+*/
+void Q3DateTimeEdit::resizeEvent(QResizeEvent *)
+{
+ int dw = de->sizeHint().width();
+ int tw = te->sizeHint().width();
+ int w = width();
+ int h = height();
+ int extra = w - (dw + tw);
+
+ if (tw + extra < 0) {
+ dw = w;
+ } else {
+ dw += 9 * extra / 16;
+ }
+ tw = w - dw;
+
+ de->setGeometry(0, 0, dw, h);
+ te->setGeometry(dw, 0, tw, h);
+}
+
+/*! \reimp
+*/
+
+QSize Q3DateTimeEdit::minimumSizeHint() const
+{
+ QSize dsh = de->minimumSizeHint();
+ QSize tsh = te->minimumSizeHint();
+ return QSize(dsh.width() + tsh.width(),
+ qMax(dsh.height(), tsh.height()));
+}
+
+/*! \internal
+ */
+
+void Q3DateTimeEdit::init()
+{
+ d = new Q3DateTimeEditPrivate();
+ de = new Q3DateEdit(this, "qt_datetime_dateedit");
+ te = new Q3TimeEdit(this, "qt_datetime_timeedit");
+ d->adv = false;
+ connect(de, SIGNAL(valueChanged(QDate)), this, SLOT(newValue(QDate)));
+ connect(te, SIGNAL(valueChanged(QTime)), this, SLOT(newValue(QTime)));
+ setFocusProxy(de);
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+}
+
+/*! \reimp
+ */
+
+QSize Q3DateTimeEdit::sizeHint() const
+{
+ ensurePolished();
+ QSize dsh = de->sizeHint();
+ QSize tsh = te->sizeHint();
+ return QSize(dsh.width() + tsh.width(),
+ qMax(dsh.height(), tsh.height()));
+}
+
+/*!
+ \property Q3DateTimeEdit::dateTime
+ \brief the editor's datetime value
+
+ The datetime edit's datetime which may be an invalid datetime.
+*/
+
+void Q3DateTimeEdit::setDateTime(const QDateTime & dt)
+{
+ if (dt.isValid()) {
+ de->setDate(dt.date());
+ te->setTime(dt.time());
+ emit valueChanged(dt);
+ }
+}
+
+QDateTime Q3DateTimeEdit::dateTime() const
+{
+ return QDateTime(de->date(), te->time());
+}
+
+/*!
+ \fn void Q3DateTimeEdit::valueChanged(const QDateTime& datetime)
+
+ This signal is emitted every time the date or time changes. The \a
+ datetime argument is the new datetime.
+*/
+
+
+/*! \internal
+
+ Re-emits the value \a d.
+ */
+
+void Q3DateTimeEdit::newValue(const QDate&)
+{
+ QDateTime dt = dateTime();
+ emit valueChanged(dt);
+}
+
+/*! \internal
+ \overload
+ Re-emits the value \a t.
+ */
+
+void Q3DateTimeEdit::newValue(const QTime&)
+{
+ QDateTime dt = dateTime();
+ emit valueChanged(dt);
+}
+
+
+/*!
+ Sets the auto advance property of the editor to \a advance. If set
+ to true, the editor will automatically advance focus to the next
+ date or time section if the user has completed a section.
+*/
+
+void Q3DateTimeEdit::setAutoAdvance(bool advance)
+{
+ de->setAutoAdvance(advance);
+ te->setAutoAdvance(advance);
+}
+
+/*!
+ Returns true if auto-advance is enabled, otherwise returns false.
+
+ \sa setAutoAdvance()
+*/
+
+bool Q3DateTimeEdit::autoAdvance() const
+{
+ return de->autoAdvance();
+}
+
+/*!
+ \fn Q3DateEdit* Q3DateTimeEdit::dateEdit()
+
+ Returns the internal widget used for editing the date part of the
+ datetime.
+*/
+
+/*!
+ \fn Q3TimeEdit* Q3DateTimeEdit::timeEdit()
+
+ Returns the internal widget used for editing the time part of the
+ datetime.
+*/
+
+QT_END_NAMESPACE
+
+#include "q3datetimeedit.moc"
+
+#endif
diff --git a/src/qt3support/widgets/q3datetimeedit.h b/src/qt3support/widgets/q3datetimeedit.h
new file mode 100644
index 0000000..9a2da91
--- /dev/null
+++ b/src/qt3support/widgets/q3datetimeedit.h
@@ -0,0 +1,288 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DATETIMEEDIT_H
+#define Q3DATETIMEEDIT_H
+
+#include <QtGui/qwidget.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qdatetime.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_DATETIMEEDIT
+
+class Q_COMPAT_EXPORT Q3DateTimeEditBase : public QWidget
+{
+ Q_OBJECT
+public:
+ Q3DateTimeEditBase(QWidget* parent=0, const char* name=0)
+ : QWidget(parent) { setObjectName(QString::fromAscii(name)); }
+
+ virtual bool setFocusSection(int sec) = 0;
+ virtual QString sectionFormattedText(int sec) = 0;
+ virtual void addNumber(int sec, int num) = 0;
+ virtual void removeLastNumber(int sec) = 0;
+
+public Q_SLOTS:
+ virtual void stepUp() = 0;
+ virtual void stepDown() = 0;
+
+private:
+ Q_DISABLE_COPY(Q3DateTimeEditBase)
+};
+
+class Q3DateEditPrivate;
+
+class Q_COMPAT_EXPORT Q3DateEdit : public Q3DateTimeEditBase
+{
+ Q_OBJECT
+ Q_ENUMS(Order)
+ Q_PROPERTY(Order order READ order WRITE setOrder)
+ Q_PROPERTY(QDate date READ date WRITE setDate USER true)
+ Q_PROPERTY(bool autoAdvance READ autoAdvance WRITE setAutoAdvance)
+ Q_PROPERTY(QDate maxValue READ maxValue WRITE setMaxValue)
+ Q_PROPERTY(QDate minValue READ minValue WRITE setMinValue)
+
+public:
+ Q3DateEdit(QWidget* parent=0, const char* name=0);
+ Q3DateEdit(const QDate& date, QWidget* parent=0, const char* name=0);
+ ~Q3DateEdit();
+
+ enum Order { DMY, MDY, YMD, YDM };
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+public Q_SLOTS:
+ virtual void setDate(const QDate& date);
+
+public:
+ QDate date() const;
+ virtual void setOrder(Order order);
+ Order order() const;
+ virtual void setAutoAdvance(bool advance);
+ bool autoAdvance() const;
+
+ virtual void setMinValue(const QDate& d) { setRange(d, maxValue()); }
+ QDate minValue() const;
+ virtual void setMaxValue(const QDate& d) { setRange(minValue(), d); }
+ QDate maxValue() const;
+ virtual void setRange(const QDate& min, const QDate& max);
+ QString separator() const;
+ virtual void setSeparator(const QString& s);
+
+ // Make removeFirstNumber() virtual in Q3DateTimeEditBase in 4.0
+ void removeFirstNumber(int sec);
+
+Q_SIGNALS:
+ void valueChanged(const QDate& date);
+
+protected:
+ bool event(QEvent *e);
+ void timerEvent(QTimerEvent *);
+ void resizeEvent(QResizeEvent *);
+ void stepUp();
+ void stepDown();
+ QString sectionFormattedText(int sec);
+ void addNumber(int sec, int num);
+
+ void removeLastNumber(int sec);
+ bool setFocusSection(int s);
+
+ virtual void setYear(int year);
+ virtual void setMonth(int month);
+ virtual void setDay(int day);
+ virtual void fix();
+ virtual bool outOfRange(int y, int m, int d) const;
+
+protected Q_SLOTS:
+ void updateButtons();
+
+private:
+ Q_DISABLE_COPY(Q3DateEdit)
+
+ void init();
+ int sectionOffsetEnd(int sec) const;
+ int sectionLength(int sec) const;
+ QString sectionText(int sec) const;
+ Q3DateEditPrivate* d;
+};
+
+class Q3TimeEditPrivate;
+
+class Q_COMPAT_EXPORT Q3TimeEdit : public Q3DateTimeEditBase
+{
+ Q_OBJECT
+ Q_FLAGS(Display)
+ Q_PROPERTY(QTime time READ time WRITE setTime USER true)
+ Q_PROPERTY(bool autoAdvance READ autoAdvance WRITE setAutoAdvance)
+ Q_PROPERTY(QTime maxValue READ maxValue WRITE setMaxValue)
+ Q_PROPERTY(QTime minValue READ minValue WRITE setMinValue)
+ Q_PROPERTY(Display display READ display WRITE setDisplay)
+
+public:
+ enum Display {
+ Hours = 0x01,
+ Minutes = 0x02,
+ Seconds = 0x04,
+ /*Reserved = 0x08,*/
+ AMPM = 0x10
+ };
+
+ Q3TimeEdit(QWidget* parent=0, const char* name=0);
+ Q3TimeEdit(const QTime& time, QWidget* parent=0, const char* name=0);
+ ~Q3TimeEdit();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+public Q_SLOTS:
+ virtual void setTime(const QTime& time);
+
+public:
+ QTime time() const;
+ virtual void setAutoAdvance(bool advance);
+ bool autoAdvance() const;
+
+ virtual void setMinValue(const QTime& d) { setRange(d, maxValue()); }
+ QTime minValue() const;
+ virtual void setMaxValue(const QTime& d) { setRange(minValue(), d); }
+ QTime maxValue() const;
+ virtual void setRange(const QTime& min, const QTime& max);
+ QString separator() const;
+ virtual void setSeparator(const QString& s);
+
+ uint display() const;
+ void setDisplay(uint disp);
+
+ // Make removeFirstNumber() virtual in Q3DateTimeEditBase in 4.0
+ void removeFirstNumber(int sec);
+
+Q_SIGNALS:
+ void valueChanged(const QTime& time);
+
+protected:
+ bool event(QEvent *e);
+ void timerEvent(QTimerEvent *e);
+ void resizeEvent(QResizeEvent *);
+ void stepUp();
+ void stepDown();
+ QString sectionFormattedText(int sec);
+ void addNumber(int sec, int num);
+ void removeLastNumber(int sec);
+ bool setFocusSection(int s);
+
+ virtual bool outOfRange(int h, int m, int s) const;
+ virtual void setHour(int h);
+ virtual void setMinute(int m);
+ virtual void setSecond(int s);
+
+protected Q_SLOTS:
+ void updateButtons();
+
+private:
+ Q_DISABLE_COPY(Q3TimeEdit)
+
+ void init();
+ QString sectionText(int sec);
+ Q3TimeEditPrivate* d;
+};
+
+
+class Q3DateTimeEditPrivate;
+
+class Q_COMPAT_EXPORT Q3DateTimeEdit : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime USER true)
+
+public:
+ Q3DateTimeEdit(QWidget* parent=0, const char* name=0);
+ Q3DateTimeEdit(const QDateTime& datetime, QWidget* parent=0,
+ const char* name=0);
+ ~Q3DateTimeEdit();
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+public Q_SLOTS:
+ virtual void setDateTime(const QDateTime & dt);
+
+public:
+ QDateTime dateTime() const;
+
+ Q3DateEdit* dateEdit() { return de; }
+ Q3TimeEdit* timeEdit() { return te; }
+
+ virtual void setAutoAdvance(bool advance);
+ bool autoAdvance() const;
+
+Q_SIGNALS:
+ void valueChanged(const QDateTime& datetime);
+
+protected:
+ void init();
+ void resizeEvent(QResizeEvent *);
+
+protected Q_SLOTS:
+ void newValue(const QDate& d);
+ void newValue(const QTime& t);
+
+private:
+ Q_DISABLE_COPY(Q3DateTimeEdit)
+
+ Q3DateEdit* de;
+ Q3TimeEdit* te;
+ Q3DateTimeEditPrivate* d;
+};
+
+#endif // QT_NO_DATETIMEEDIT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DATETIMEEDIT_H
diff --git a/src/qt3support/widgets/q3dockarea.cpp b/src/qt3support/widgets/q3dockarea.cpp
new file mode 100644
index 0000000..0fce67b
--- /dev/null
+++ b/src/qt3support/widgets/q3dockarea.cpp
@@ -0,0 +1,1351 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dockarea.h"
+
+#ifndef QT_NO_MAINWINDOW
+#include "qsplitter.h"
+#include "qevent.h"
+#include "qlayout.h"
+#include "qapplication.h"
+#include "qpainter.h"
+#include "qmap.h"
+#include "q3mainwindow.h"
+#include "q3toolbar.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define QDOCKAREA_DEBUG
+
+struct Q3DockData
+{
+ Q3DockData() : w(0), rect() {}
+ Q3DockData(Q3DockWindow *dw, const QRect &r) : w(dw), rect(r) {}
+ Q3DockWindow *w;
+ QRect rect;
+
+ Q_DUMMY_COMPARISON_OPERATOR(Q3DockData)
+};
+
+static int fix_x(Q3DockWindow* w, int width = -1) {
+ if (QApplication::reverseLayout()) {
+ if (width < 0)
+ width = w->width();
+ return w->parentWidget()->width() - w->x() - width;
+ }
+ return w->x();
+}
+static int fix_x(Q3DockWindow* w, int x, int width = -1) {
+ if (QApplication::reverseLayout()) {
+ if (width < 0)
+ width = w->width();
+ return w->parentWidget()->width() - x - width;
+ }
+ return x;
+}
+
+static QPoint fix_pos(Q3DockWindow* w) {
+ if (QApplication::reverseLayout()) {
+ QPoint p = w->pos();
+ p.rx() = w->parentWidget()->width() - p.x() - w->width();
+ return p;
+ }
+ return w->pos();
+}
+
+
+void Q3DockAreaLayout::setGeometry(const QRect &r)
+{
+ QLayout::setGeometry(r);
+ layoutItems(r);
+}
+
+QLayoutItem *Q3DockAreaLayout::itemAt(int) const
+{
+ return 0; //###
+}
+
+QLayoutItem *Q3DockAreaLayout::takeAt(int)
+{
+ return 0; //###
+}
+
+int Q3DockAreaLayout::count() const
+{
+ return 0; //###
+}
+
+
+QSize Q3DockAreaLayout::sizeHint() const
+{
+ if (dockWindows->isEmpty())
+ return QSize(0, 0);
+
+ if (dirty) {
+ Q3DockAreaLayout *that = (Q3DockAreaLayout *) this;
+ that->layoutItems(geometry());
+ }
+
+ int w = 0;
+ int h = 0;
+ int y = -1;
+ int x = -1;
+ int ph = 0;
+ int pw = 0;
+ for (int i = 0; i < dockWindows->size(); ++i) {
+ Q3DockWindow *dw = dockWindows->at(i);
+ int plush = 0, plusw = 0;
+ if (dw->isHidden())
+ continue;
+ if (hasHeightForWidth()) {
+ if (y != dw->y())
+ plush = ph;
+ y = dw->y();
+ ph = dw->height();
+ } else {
+ if (x != dw->x())
+ plusw = pw;
+ x = dw->x();
+ pw = dw->width();
+ }
+ h = qMax(h, dw->height() + plush);
+ w = qMax(w, dw->width() + plusw);
+ }
+
+ if (hasHeightForWidth())
+ return QSize(0, h);
+ return QSize(w, 0);
+}
+
+bool Q3DockAreaLayout::hasHeightForWidth() const
+{
+ return orient == Qt::Horizontal;
+}
+
+void Q3DockAreaLayout::init()
+{
+ dirty = true;
+ cached_width = 0;
+ cached_height = 0;
+ cached_hfw = -1;
+ cached_wfh = -1;
+}
+
+void Q3DockAreaLayout::invalidate()
+{
+ dirty = true;
+ cached_width = 0;
+ cached_height = 0;
+ QLayout::invalidate();
+}
+
+static int start_pos(const QRect &r, Qt::Orientation o)
+{
+ if (o == Qt::Horizontal) {
+ return qMax(0, r.x());
+ } else {
+ return qMax(0, r.y());
+ }
+}
+
+static void add_size(int s, int &pos, Qt::Orientation o)
+{
+ if (o == Qt::Horizontal) {
+ pos += s;
+ } else {
+ pos += s;
+ }
+}
+
+static int space_left(const QRect &r, int pos, Qt::Orientation o)
+{
+ if (o == Qt::Horizontal) {
+ return (r.x() + r.width()) - pos;
+ } else {
+ return (r.y() + r.height()) - pos;
+ }
+}
+
+static int dock_extent(Q3DockWindow *w, Qt::Orientation o, int maxsize)
+{
+ if (o == Qt::Horizontal)
+ return qMin(maxsize, qMax(w->sizeHint().width(), w->fixedExtent().width()));
+ else
+ return qMin(maxsize, qMax(w->sizeHint().height(), w->fixedExtent().height()));
+}
+
+static int dock_strut(Q3DockWindow *w, Qt::Orientation o)
+{
+ if (o != Qt::Horizontal) {
+ int wid;
+ if ((wid = w->fixedExtent().width()) != -1)
+ return qMax(wid, qMax(w->minimumSize().width(), w->minimumSizeHint().width()));
+ return qMax(w->sizeHint().width(), qMax(w->minimumSize().width(), w->minimumSizeHint().width()));
+ } else {
+ int hei;
+ if ((hei = w->fixedExtent().height()) != -1)
+ return qMax(hei, qMax(w->minimumSizeHint().height(), w->minimumSize().height()));
+ return qMax(w->sizeHint().height(), qMax(w->minimumSizeHint().height(), w->minimumSize().height()));
+ }
+}
+
+static void set_geometry(Q3DockWindow *w, int pos, int sectionpos, int extent, int strut, Qt::Orientation o)
+{
+ if (o == Qt::Horizontal)
+ w->setGeometry(fix_x(w, pos, extent), sectionpos, extent, strut);
+ else
+ w->setGeometry(sectionpos, pos, strut, extent);
+}
+
+static int size_extent(const QSize &s, Qt::Orientation o, bool swap = false)
+{
+ return o == Qt::Horizontal ? (swap ? s.height() : s.width()) : (swap ? s.width() : s.height());
+}
+
+static int point_pos(const QPoint &p, Qt::Orientation o, bool swap = false)
+{
+ return o == Qt::Horizontal ? (swap ? p.y() : p.x()) : (swap ? p.x() : p.y());
+}
+
+static void shrink_extend(Q3DockWindow *dw, int &dockExtend, int /*spaceLeft*/, Qt::Orientation o)
+{
+ Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(dw);
+ if (o == Qt::Horizontal) {
+ int mw = 0;
+ if (!tb)
+ mw = dw->minimumWidth();
+ else
+ mw = dw->sizeHint().width();
+ dockExtend = mw;
+ } else {
+ int mh = 0;
+ if (!tb)
+ mh = dw->minimumHeight();
+ else
+ mh = dw->sizeHint().height();
+ dockExtend = mh;
+ }
+}
+
+static void place_line(QList<Q3DockData> &lastLine, Qt::Orientation o, int linestrut, int fullextent, int tbstrut, int maxsize, Q3DockAreaLayout *)
+{
+ Q3DockWindow *last = 0;
+ QRect lastRect;
+ for (QList<Q3DockData>::Iterator it = lastLine.begin(); it != lastLine.end(); ++it) {
+ if (tbstrut != -1 && qobject_cast<Q3ToolBar*>((*it).w))
+ (*it).rect.setHeight(tbstrut);
+ if (!last) {
+ last = (*it).w;
+ lastRect = (*it).rect;
+ continue;
+ }
+ if (!last->isStretchable()) {
+ int w = qMin(lastRect.width(), maxsize);
+ set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o);
+ } else {
+ int w = qMin((*it).rect.x() - lastRect.x(), maxsize);
+ set_geometry(last, lastRect.x(), lastRect.y(), w,
+ last->isResizeEnabled() ? linestrut : lastRect.height(), o);
+ }
+ last = (*it).w;
+ lastRect = (*it).rect;
+ }
+ if (!last)
+ return;
+ if (!last->isStretchable()) {
+ int w = qMin(lastRect.width(), maxsize);
+ set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o);
+ } else {
+ int w = qMin(fullextent - lastRect.x() - (o == Qt::Vertical ? 1 : 0), maxsize);
+ set_geometry(last, lastRect.x(), lastRect.y(), w,
+ last->isResizeEnabled() ? linestrut : lastRect.height(), o);
+ }
+}
+
+QSize Q3DockAreaLayout::minimumSize() const
+{
+ if (dockWindows->isEmpty())
+ return QSize(0, 0);
+
+ if (dirty) {
+ Q3DockAreaLayout *that = (Q3DockAreaLayout *) this;
+ that->layoutItems(geometry());
+ }
+
+ int s = 0;
+
+ for (int i = 0; i < dockWindows->size(); ++i) {
+ Q3DockWindow *dw = dockWindows->at(i);
+ if (dw->isHidden())
+ continue;
+ s = qMax(s, dock_strut(dw, orientation()));
+ }
+
+ return orientation() == Qt::Horizontal ? QSize(0, s ? s+2 : 0) : QSize(s, 0);
+}
+
+
+
+int Q3DockAreaLayout::layoutItems(const QRect &rect, bool testonly)
+{
+ if (dockWindows->isEmpty())
+ return 0;
+
+ dirty = false;
+
+ // some corrections
+ QRect r = rect;
+ if (orientation() == Qt::Vertical)
+ r.setHeight(r.height() - 3);
+
+ // init
+ lines.clear();
+ ls.clear();
+ int start = start_pos(r, orientation());
+ int pos = start;
+ int sectionpos = 0;
+ int linestrut = 0;
+ QList<Q3DockData> lastLine;
+ int tbstrut = -1;
+ int maxsize = size_extent(rect.size(), orientation());
+ int visibleWindows = 0;
+
+ // go through all widgets in the dock
+ for (int i = 0; i < dockWindows->size(); ++i) {
+ Q3DockWindow *dw = dockWindows->at(i);
+ if (dw->isHidden())
+ continue;
+ ++visibleWindows;
+ // find position for the widget: This is the maximum of the
+ // end of the previous widget and the offset of the widget. If
+ // the position + the width of the widget dosn't fit into the
+ // dock, try moving it a bit back, if possible.
+ int op = pos;
+ int dockExtend = dock_extent(dw, orientation(), maxsize);
+ if (!dw->isStretchable()) {
+ pos = qMax(pos, dw->offset());
+ if (pos + dockExtend > size_extent(r.size(), orientation()) - 1)
+ pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend);
+ }
+ if (!lastLine.isEmpty() && !dw->newLine() && space_left(rect, pos, orientation()) < dockExtend)
+ shrink_extend(dw, dockExtend, space_left(rect, pos, orientation()), orientation());
+ // if the current widget doesn't fit into the line anymore and it is not the first widget of the line
+ if (!lastLine.isEmpty() &&
+ (space_left(rect, pos, orientation()) < dockExtend || dw->newLine())) {
+ if (!testonly) // place the last line, if not in test mode
+ place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this);
+ // remember the line coordinats of the last line
+ if (orientation() == Qt::Horizontal)
+ lines.append(QRect(0, sectionpos, r.width(), linestrut));
+ else
+ lines.append(QRect(sectionpos, 0, linestrut, r.height()));
+ // do some clearing for the next line
+ lastLine.clear();
+ sectionpos += linestrut;
+ linestrut = 0;
+ pos = start;
+ tbstrut = -1;
+ }
+
+ // remember first widget of a line
+ if (lastLine.isEmpty()) {
+ ls.append(dw);
+ // try to make the best position
+ int op = pos;
+ if (!dw->isStretchable())
+ pos = qMax(pos, dw->offset());
+ if (pos + dockExtend > size_extent(r.size(), orientation()) - 1)
+ pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend);
+ }
+ // do some calculations and add the remember the rect which the docking widget requires for the placing
+ QRect dwRect(pos, sectionpos, dockExtend, dock_strut(dw, orientation() ));
+ lastLine.append(Q3DockData(dw, dwRect));
+ if (qobject_cast<Q3ToolBar*>(dw))
+ tbstrut = qMax(tbstrut, dock_strut(dw, orientation()));
+ linestrut = qMax(dock_strut(dw, orientation()), linestrut);
+ add_size(dockExtend, pos, orientation());
+ }
+
+ // if some stuff was not placed/stored yet, do it now
+ if (!testonly)
+ place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this);
+ if (orientation() == Qt::Horizontal)
+ lines.append(QRect(0, sectionpos, r.width(), linestrut));
+ else
+ lines.append(QRect(sectionpos, 0, linestrut, r.height()));
+ if (lines.size() >= 2 && *(--lines.end()) == *(--(--lines.end())))
+ lines.removeLast();
+
+ bool hadResizable = false;
+ for (int i = 0; i < dockWindows->size(); ++i) {
+ Q3DockWindow *dw = dockWindows->at(i);
+ if (!dw->isVisibleTo(parentWidget))
+ continue;
+ hadResizable = hadResizable || dw->isResizeEnabled();
+ dw->updateSplitterVisibility(visibleWindows > 1); //!dw->area()->isLastDockWindow(dw));
+ if (Q3ToolBar *tb = qobject_cast<Q3ToolBar *>(dw))
+ tb->checkForExtension(dw->size());
+ }
+ return sectionpos + linestrut;
+}
+
+int Q3DockAreaLayout::heightForWidth(int w) const
+{
+ if (dockWindows->isEmpty() && parentWidget)
+ return parentWidget->minimumHeight();
+
+ if (cached_width != w) {
+ Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this;
+ mthis->cached_width = w;
+ int h = mthis->layoutItems(QRect(0, 0, w, 0), true);
+ mthis->cached_hfw = h;
+ return h;
+ }
+
+ return cached_hfw;
+}
+
+int Q3DockAreaLayout::widthForHeight(int h) const
+{
+ if (cached_height != h) {
+ Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this;
+ mthis->cached_height = h;
+ int w = mthis->layoutItems(QRect(0, 0, 0, h), true);
+ mthis->cached_wfh = w;
+ return w;
+ }
+ return cached_wfh;
+}
+
+
+
+
+/*!
+ \class Q3DockArea
+ \brief The Q3DockArea class manages and lays out Q3DockWindows.
+
+ \compat
+
+ A Q3DockArea is a container which manages a list of
+ \l{Q3DockWindow}s which it lays out within its area. In cooperation
+ with the \l{Q3DockWindow}s it is responsible for the docking and
+ undocking of \l{Q3DockWindow}s and moving them inside the dock
+ area. Q3DockAreas also handle the wrapping of \l{Q3DockWindow}s to
+ fill the available space as compactly as possible. Q3DockAreas can
+ contain Q3ToolBars since Q3ToolBar is a Q3DockWindow subclass.
+
+ QMainWindow contains four Q3DockAreas which you can use for your
+ Q3ToolBars and Q3DockWindows, so in most situations you do not
+ need to use the Q3DockArea class directly. Although QMainWindow
+ contains support for its own dock areas, you can't add new ones.
+ You also can't add a Q3DockArea to your own subclass of QWidget.
+ It won't be shown.
+
+ \img qmainwindow-qdockareas.png QMainWindow's Q3DockAreas
+
+ \target lines
+ \section1 Lines.
+
+ Q3DockArea uses the concept of lines. A line is a horizontal
+ region which may contain dock windows side-by-side. A dock area
+ may have room for more than one line. When dock windows are docked
+ into a dock area they are usually added at the right hand side of
+ the top-most line that has room (unless manually placed by the
+ user). When users move dock windows they may leave empty lines or
+ gaps in non-empty lines. Qt::Dock windows can be lined up to
+ minimize wasted space using the lineUp() function.
+
+ The Q3DockArea class maintains a position list of all its child
+ dock windows. Qt::Dock windows are added to a dock area from position
+ 0 onwards. Qt::Dock windows are laid out sequentially in position
+ order from left to right, and in the case of multiple lines of
+ dock windows, from top to bottom. If a dock window is floated it
+ still retains its position since this is where the window will
+ return if the user double clicks its caption. A dock window's
+ position can be determined with hasDockWindow(). The position can
+ be changed with moveDockWindow().
+
+ To dock or undock a dock window use Q3DockWindow::dock() and
+ Q3DockWindow::undock() respectively. If you want to control which
+ dock windows can dock in a dock area use setAcceptDockWindow(). To
+ see if a dock area contains a particular dock window use
+ \l{hasDockWindow()}; to see how many dock windows a dock area
+ contains use count().
+
+ The streaming operators can write the positions of the dock
+ windows in the dock area to a QTextStream. The positions can be
+ read back later to restore the saved positions.
+
+ Save the positions to a QTextStream:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 0
+
+ Restore the positions from a QTextStream:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 1
+*/
+
+/*!
+ \property Q3DockArea::handlePosition
+ \brief where the dock window splitter handle is placed in the dock
+ area
+
+ The default position is \c Normal.
+*/
+
+/*!
+ \property Q3DockArea::orientation
+ \brief the dock area's orientation
+
+ There is no default value; the orientation is specified in the
+ constructor.
+*/
+
+/*!
+ \enum Q3DockArea::HandlePosition
+
+ A dock window has two kinds of handles, the dock window handle
+ used for dragging the dock window, and the splitter handle used to
+ resize the dock window in relation to other dock windows using a
+ splitter. (The splitter handle is only visible for docked
+ windows.)
+
+ This enum specifies where the dock window splitter handle is
+ placed in the dock area.
+
+ \value Normal The splitter handles of dock windows are placed at
+ the right or bottom.
+
+ \value Reverse The splitter handles of dock windows are placed at
+ the left or top.
+*/
+
+/*!
+ Constructs a Q3DockArea with orientation \a o, HandlePosition \a h,
+ parent \a parent and called \a name.
+*/
+
+Q3DockArea::Q3DockArea(Qt::Orientation o, HandlePosition h, QWidget *parent, const char *name)
+ : QWidget(parent, name), orient(o), layout(0), hPos(h)
+{
+ layout = new Q3DockAreaLayout(this, o, &dockWindows, 0, 0, "toollayout");
+ installEventFilter(this);
+}
+
+/*!
+ Destroys the dock area and all the dock windows docked in the dock
+ area.
+
+ Does not affect any floating dock windows or dock windows in other
+ dock areas, even if they first appeared in this dock area.
+ Floating dock windows are effectively top level windows and are
+ not child windows of the dock area. When a floating dock window is
+ docked (dragged into a dock area) its parent becomes the dock
+ area.
+*/
+
+Q3DockArea::~Q3DockArea()
+{
+ while (!dockWindows.isEmpty())
+ delete dockWindows.takeFirst();
+}
+
+/*!
+ Moves the Q3DockWindow \a w within the dock area. If \a w is not
+ already docked in this area, \a w is docked first. If \a index is
+ -1 or larger than the number of docked widgets, \a w is appended
+ at the end, otherwise it is inserted at the position \a index.
+*/
+
+void Q3DockArea::moveDockWindow(Q3DockWindow *w, int index)
+{
+ invalidateFixedSizes();
+ Q3DockWindow *dockWindow = 0;
+ int dockWindowIndex = findDockWindow(w);
+ if (dockWindowIndex == -1) {
+ dockWindow = w;
+ bool vis = dockWindow->isVisible();
+ dockWindow->setParent(this);
+ dockWindow->move(0, 0);
+ if(vis)
+ dockWindow->show();
+ w->installEventFilter(this);
+ updateLayout();
+ setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
+ orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
+ dockWindows.append(w);
+ } else {
+ if (w->parent() != this) {
+ bool vis = w->isVisible();
+ w->setParent(this);
+ w->move(0, 0);
+ if(vis)
+ w->show();
+ }
+ if (index == -1) {
+ dockWindows.removeAll(w);
+ dockWindows.append(w);
+ }
+ }
+
+ w->dockArea = this;
+ w->curPlace = Q3DockWindow::InDock;
+ w->updateGui();
+
+ if (index != -1 && index < (int)dockWindows.count()) {
+ dockWindows.removeAll(w);
+ dockWindows.insert(index, w);
+ }
+}
+
+/*!
+ Returns true if the dock area contains the dock window \a w;
+ otherwise returns false. If \a index is not 0 it will be set as
+ follows: if the dock area contains the dock window *\a{index} is
+ set to \a w's index position; otherwise *\a{index} is set to -1.
+*/
+
+bool Q3DockArea::hasDockWindow(Q3DockWindow *w, int *index)
+{
+ int i = dockWindows.indexOf(w);
+ if (index)
+ *index = i;
+ return i != -1;
+}
+
+int Q3DockArea::lineOf(int index)
+{
+ QList<Q3DockWindow *> lineStarts = layout->lineStarts();
+ int i = 0;
+ for (; i < lineStarts.size(); ++i) {
+ Q3DockWindow *w = lineStarts.at(i);
+ if (dockWindows.indexOf(w) >= index)
+ return i;
+ }
+ return i;
+}
+
+/*!
+ \overload
+
+ Moves the dock window \a w inside the dock area where \a p is the
+ new position (in global screen coordinates), \a r is the suggested
+ rectangle of the dock window and \a swap specifies whether or not
+ the orientation of the docked widget needs to be changed.
+
+ This function is used internally by Q3DockWindow. You shouldn't
+ need to call it yourself.
+*/
+
+void Q3DockArea::moveDockWindow(Q3DockWindow *w, const QPoint &p, const QRect &r, bool swap)
+{
+ invalidateFixedSizes();
+ int mse = -10;
+ bool hasResizable = false;
+ for (int i = 0; i < dockWindows.size(); ++i) {
+ Q3DockWindow *dw = dockWindows.at(i);
+ if (dw->isHidden())
+ continue;
+ if (dw->isResizeEnabled())
+ hasResizable = true;
+ if (orientation() != Qt::Horizontal)
+ mse = qMax(qMax(dw->fixedExtent().width(), dw->width()), mse);
+ else
+ mse = qMax(qMax(dw->fixedExtent().height(), dw->height()), mse);
+ }
+ if (!hasResizable && w->isResizeEnabled()) {
+ if (orientation() != Qt::Horizontal)
+ mse = qMax(w->fixedExtent().width(), mse);
+ else
+ mse = qMax(w->fixedExtent().height(), mse);
+ }
+
+ Q3DockWindow *dockWindow = 0;
+ int dockWindowIndex = findDockWindow(w);
+ QList<Q3DockWindow *> lineStarts = layout->lineStarts();
+ QList<QRect> lines = layout->lineList();
+ bool wasAloneInLine = false;
+ QPoint pos = mapFromGlobal(p);
+ int line = lineOf(dockWindowIndex);
+ QRect lr;
+ if (line < lines.size())
+ lr = lines.at(line);
+ if (dockWindowIndex != -1) {
+ if (lineStarts.contains(w)
+ && ((dockWindowIndex < dockWindows.count() - 1
+ && lineStarts.contains(dockWindows.at(dockWindowIndex + 1)))
+ || dockWindowIndex == dockWindows.count() - 1))
+ wasAloneInLine = true;
+ dockWindow = dockWindows.takeAt(dockWindowIndex);
+ if (!wasAloneInLine) { // only do the pre-layout if the widget isn't the only one in its line
+ if (lineStarts.contains(dockWindow) && dockWindowIndex < dockWindows.count())
+ dockWindows.at(dockWindowIndex)->setNewLine(true);
+ layout->layoutItems(QRect(0, 0, width(), height()), true);
+ }
+ } else {
+ dockWindow = w;
+ bool vis = dockWindow->isVisible();
+ dockWindow->setParent(this);
+ dockWindow->move(0, 0);
+ if(vis)
+ dockWindow->show();
+ if (swap)
+ dockWindow->resize(dockWindow->height(), dockWindow->width());
+ w->installEventFilter(this);
+ }
+
+ lineStarts = layout->lineStarts();
+ lines = layout->lineList();
+
+ QRect rect = QRect(mapFromGlobal(r.topLeft()), r.size());
+ if (orientation() == Qt::Horizontal && QApplication::reverseLayout()) {
+ rect = QRect(width() - rect.x() - rect.width(), rect.y(), rect.width(), rect.height());
+ pos.rx() = width() - pos.x();
+ }
+ dockWindow->setOffset(point_pos(rect.topLeft(), orientation()));
+ if (orientation() == Qt::Horizontal) {
+ int offs = dockWindow->offset();
+ if (width() - offs < dockWindow->minimumWidth())
+ dockWindow->setOffset(width() - dockWindow->minimumWidth());
+ } else {
+ int offs = dockWindow->offset();
+ if (height() - offs < dockWindow->minimumHeight())
+ dockWindow->setOffset(height() - dockWindow->minimumHeight());
+ }
+
+ if (dockWindows.isEmpty()) {
+ dockWindows.append(dockWindow);
+ } else {
+ int dockLine = -1;
+ bool insertLine = false;
+ int i = 0;
+ QRect lineRect;
+ // find the line which we touched with the mouse
+ for (QList<QRect>::Iterator it = lines.begin(); it != lines.end(); ++it, ++i) {
+ if (point_pos(pos, orientation(), true) >= point_pos((*it).topLeft(), orientation(), true) &&
+ point_pos(pos, orientation(), true) <= point_pos((*it).topLeft(), orientation(), true) +
+ size_extent((*it).size(), orientation(), true)) {
+ dockLine = i;
+ lineRect = *it;
+ break;
+ }
+ }
+ if (dockLine == -1) { // outside the dock...
+ insertLine = true;
+ if (point_pos(pos, orientation(), true) < 0) // insert as first line
+ dockLine = 0;
+ else
+ dockLine = (int)lines.count(); // insert after the last line ### size_t/int cast
+ } else { // inside the dock (we have found a dockLine)
+ if (point_pos(pos, orientation(), true) <
+ point_pos(lineRect.topLeft(), orientation(), true) + 4) { // mouse was at the very beginning of the line
+ insertLine = true; // insert a new line before that with the docking widget
+ } else if (point_pos(pos, orientation(), true) >
+ point_pos(lineRect.topLeft(), orientation(), true) +
+ size_extent(lineRect.size(), orientation(), true) - 4) { // mouse was at the very and of the line
+ insertLine = true; // insert a line after that with the docking widget
+ dockLine++;
+ }
+ }
+
+ if (!insertLine && wasAloneInLine && lr.contains(pos)) // if we are alone in a line and just moved in there, re-insert it
+ insertLine = true;
+
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("insert in line %d, and insert that line: %d", dockLine, insertLine);
+ qDebug(" (btw, we have %d lines)", lines.count());
+#endif
+ Q3DockWindow *dw = 0;
+ if (dockLine >= (int)lines.count()) { // insert after last line
+ dockWindows.append(dockWindow);
+ dockWindow->setNewLine(true);
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("insert at the end");
+#endif
+ } else if (dockLine == 0 && insertLine) { // insert before first line
+ dockWindows.insert(0, dockWindow);
+ dockWindows.at(1)->setNewLine(true);
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("insert at the begin");
+#endif
+ } else { // insert somewhere in between
+ // make sure each line start has a new line
+ for (int i = 0; i < lineStarts.size(); ++i) {
+ dw = lineStarts.at(i);
+ dw->setNewLine(true);
+ }
+
+ // find the index of the first widget in the search line
+ int searchLine = dockLine;
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("search line start of %d", searchLine);
+#endif
+ Q3DockWindow *lsw = lineStarts.at(searchLine);
+ int index = dockWindows.indexOf(lsw);
+ if (index == -1) { // the linestart widget hasn't been found, try to find it harder
+ if (lsw == w && dockWindowIndex <= dockWindows.count())
+ index = dockWindowIndex;
+ else
+ index = 0;
+ }
+#if defined(QDOCKAREA_DEBUG)
+ qDebug(" which starts at %d", index);
+#endif
+ if (!insertLine) { // if we insert the docking widget in the existing line
+ // find the index for the widget
+ bool inc = true;
+ bool firstTime = true;
+ for (int i = index; i < dockWindows.size(); ++i) {
+ dw = dockWindows.at(i);
+ if (orientation() == Qt::Horizontal)
+ dw->setFixedExtentWidth(-1);
+ else
+ dw->setFixedExtentHeight(-1);
+ if (!firstTime && lineStarts.contains(dw)) // we are in the next line, so break
+ break;
+ if (point_pos(pos, orientation()) <
+ point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2) {
+ inc = false;
+ }
+ if (inc)
+ index++;
+ firstTime = false;
+ }
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("insert at index: %d", index);
+#endif
+ // if we insert it just before a widget which has a new line, transfer the newline to the docking widget
+ // but not if we didn't only mave a widget in its line which was alone in the line before
+ if (!(wasAloneInLine && lr.contains(pos))
+ && index >= 0 && index < dockWindows.count() &&
+ dockWindows.at(index)->newLine() && lineOf(index) == dockLine) {
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("get rid of the old newline and get me one");
+#endif
+ dockWindows.at(index)->setNewLine(false);
+ dockWindow->setNewLine(true);
+ } else if (wasAloneInLine && lr.contains(pos)) {
+ dockWindow->setNewLine(true);
+ } else { // if we are somewhere in a line, get rid of the newline
+ dockWindow->setNewLine(false);
+ }
+ } else { // insert in a new line, so make sure the dock widget and the widget which will be after it have a newline
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("insert a new line");
+#endif
+ if (index < dockWindows.count()) {
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("give the widget at %d a newline", index);
+#endif
+ Q3DockWindow* nldw = dockWindows.at(index);
+ if (nldw)
+ nldw->setNewLine(true);
+ }
+#if defined(QDOCKAREA_DEBUG)
+ qDebug("give me a newline");
+#endif
+ dockWindow->setNewLine(true);
+ }
+ // finally insert the widget
+ dockWindows.insert(index, dockWindow);
+ }
+ }
+
+ if (mse != -10 && w->isResizeEnabled()) {
+ if (orientation() != Qt::Horizontal)
+ w->setFixedExtentWidth(qMin(qMax(w->minimumWidth(), mse), w->sizeHint().width()));
+ else
+ w->setFixedExtentHeight(qMin(qMax(w->minimumHeight(), mse), w->sizeHint().height()));
+ }
+
+ updateLayout();
+ setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
+ orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
+}
+
+/*!
+ Removes the dock window \a w from the dock area. If \a
+ makeFloating is true, \a w gets floated, and if \a swap is true,
+ the orientation of \a w gets swapped. If \a fixNewLines is true
+ (the default) newlines in the area will be fixed.
+
+ You should never need to call this function yourself. Use
+ Q3DockWindow::dock() and Q3DockWindow::undock() instead.
+*/
+
+void Q3DockArea::removeDockWindow(Q3DockWindow *w, bool makeFloating, bool swap, bool fixNewLines)
+{
+ w->removeEventFilter(this);
+ Q3DockWindow *dockWindow = 0;
+ int i = findDockWindow(w);
+ if (i == -1)
+ return;
+ dockWindow = dockWindows.at(i);
+ dockWindows.removeAt(i);
+ QList<Q3DockWindow *> lineStarts = layout->lineStarts();
+ if (fixNewLines && lineStarts.contains(dockWindow) && i < dockWindows.count())
+ dockWindows.at(i)->setNewLine(true);
+ if (makeFloating) {
+ QWidget *p = parentWidget() ? parentWidget() : window();
+ dockWindow->setParent(p, Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool);
+ dockWindow->move(0, 0);
+ }
+ if (swap)
+ dockWindow->resize(dockWindow->height(), dockWindow->width());
+ updateLayout();
+ if (dockWindows.isEmpty())
+ setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
+}
+
+int Q3DockArea::findDockWindow(Q3DockWindow *w)
+{
+ return dockWindows.indexOf(w);
+}
+
+void Q3DockArea::updateLayout()
+{
+ layout->invalidate();
+ layout->activate();
+}
+
+/*! \reimp
+ */
+
+bool Q3DockArea::eventFilter(QObject *o, QEvent *e)
+{
+ if (e->type() == QEvent::Close) {
+ if (qobject_cast<Q3DockWindow*>(o)) {
+ o->removeEventFilter(this);
+ QApplication::sendEvent(o, e);
+ if (((QCloseEvent*)e)->isAccepted())
+ removeDockWindow((Q3DockWindow*)o, false, false);
+ return true;
+ }
+ }
+ return false;
+}
+
+/*! \internal
+
+ Invalidates the offset of the next dock window in the dock area.
+ */
+
+void Q3DockArea::invalidNextOffset(Q3DockWindow *dw)
+{
+ int i = dockWindows.indexOf(dw);
+ if (i == -1 || i >= (int)dockWindows.count() - 1)
+ return;
+ if ((dw = dockWindows.at(++i)))
+ dw->setOffset(0);
+}
+
+/*!
+ \property Q3DockArea::count
+ \brief the number of dock windows in the dock area
+*/
+int Q3DockArea::count() const
+{
+ return dockWindows.count();
+}
+
+/*!
+ \property Q3DockArea::empty
+ \brief whether the dock area is empty
+*/
+
+bool Q3DockArea::isEmpty() const
+{
+ return dockWindows.isEmpty();
+}
+
+
+/*!
+ Returns a list of the dock windows in the dock area.
+*/
+
+QList<Q3DockWindow *> Q3DockArea::dockWindowList() const
+{
+ return dockWindows;
+}
+
+/*!
+ Lines up the dock windows in this dock area to minimize wasted
+ space. If \a keepNewLines is true, only space within lines is
+ cleaned up. If \a keepNewLines is false the number of lines might
+ be changed.
+*/
+
+void Q3DockArea::lineUp(bool keepNewLines)
+{
+ for (int i = 0; i < dockWindows.size(); ++i) {
+ Q3DockWindow *dw = dockWindows.at(i);
+ dw->setOffset(0);
+ if (!keepNewLines)
+ dw->setNewLine(false);
+ }
+ layout->invalidate();
+ layout->activate();
+}
+
+Q3DockArea::DockWindowData *Q3DockArea::dockWindowData(Q3DockWindow *w)
+{
+ DockWindowData *data = new DockWindowData;
+ data->index = findDockWindow(w);
+ if (data->index == -1) {
+ delete data;
+ return 0;
+ }
+ QList<Q3DockWindow *> lineStarts = layout->lineStarts();
+ int i = -1;
+ for (int j = 0; j < dockWindows.size(); ++j) {
+ Q3DockWindow *dw = dockWindows.at(j);
+ if (lineStarts.contains(dw))
+ ++i;
+ if (dw == w)
+ break;
+ }
+ data->line = i;
+ data->offset = point_pos(QPoint(fix_x(w), w->y()), orientation());
+ data->area = this;
+ data->fixedExtent = w->fixedExtent();
+ return data;
+}
+
+void Q3DockArea::dockWindow(Q3DockWindow *dockWindow, DockWindowData *data)
+{
+ if (!data)
+ return;
+
+ dockWindow->setParent(this);
+ dockWindow->move(0, 0);
+
+ dockWindow->installEventFilter(this);
+ dockWindow->dockArea = this;
+ dockWindow->updateGui();
+
+ if (dockWindows.isEmpty()) {
+ dockWindows.append(dockWindow);
+ } else {
+ QList<Q3DockWindow *> lineStarts = layout->lineStarts();
+ int index = 0;
+ if (lineStarts.count() > data->line)
+ index = dockWindows.indexOf(lineStarts.at(data->line));
+ if (index == -1)
+ index = 0;
+ bool firstTime = true;
+ int offset = data->offset;
+ for (int i = index; i < dockWindows.size(); ++i) {
+ Q3DockWindow *dw = dockWindows.at(i);
+ if (!firstTime && lineStarts.contains(dw))
+ break;
+ if (offset <
+ point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2)
+ break;
+ index++;
+ firstTime = false;
+ }
+ if (index >= 0 && index < dockWindows.count() &&
+ dockWindows.at(index)->newLine() && lineOf(index) == data->line) {
+ dockWindows.at(index)->setNewLine(false);
+ dockWindow->setNewLine(true);
+ } else {
+ dockWindow->setNewLine(false);
+ }
+
+ dockWindows.insert(index, dockWindow);
+ }
+ dockWindow->show();
+
+ dockWindow->setFixedExtentWidth(data->fixedExtent.width());
+ dockWindow->setFixedExtentHeight(data->fixedExtent.height());
+
+ updateLayout();
+ setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum,
+ orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum));
+
+}
+
+/*!
+ Returns true if dock window \a dw could be docked into the dock
+ area; otherwise returns false.
+
+ \sa setAcceptDockWindow()
+*/
+
+bool Q3DockArea::isDockWindowAccepted(Q3DockWindow *dw)
+{
+ if (!dw)
+ return false;
+ if (forbiddenWidgets.contains(dw))
+ return false;
+
+ Q3MainWindow *mw = qobject_cast<Q3MainWindow*>(parentWidget());
+ if (!mw)
+ return true;
+ if (!mw->hasDockWindow(dw))
+ return false;
+ if (!mw->isDockEnabled(this))
+ return false;
+ if (!mw->isDockEnabled(dw, this))
+ return false;
+ return true;
+}
+
+/*!
+ If \a accept is true, dock window \a dw can be docked in the dock
+ area. If \a accept is false, dock window \a dw cannot be docked in
+ the dock area.
+
+ \sa isDockWindowAccepted()
+*/
+
+void Q3DockArea::setAcceptDockWindow(Q3DockWindow *dw, bool accept)
+{
+ if (accept)
+ forbiddenWidgets.removeAll(dw);
+ else if (!forbiddenWidgets.contains(dw))
+ forbiddenWidgets.append(dw);
+}
+
+void Q3DockArea::invalidateFixedSizes()
+{
+ for (int i = 0; i < dockWindows.size(); ++i) {
+ Q3DockWindow *dw = dockWindows.at(i);
+ if (orientation() == Qt::Horizontal)
+ dw->setFixedExtentWidth(-1);
+ else
+ dw->setFixedExtentHeight(-1);
+ }
+}
+
+int Q3DockArea::maxSpace(int hint, Q3DockWindow *dw)
+{
+ int index = findDockWindow(dw);
+ if (index == -1 || index + 1 >= (int)dockWindows.count()) {
+ if (orientation() == Qt::Horizontal)
+ return dw->width();
+ return dw->height();
+ }
+
+ Q3DockWindow *w = 0;
+ int i = 0;
+ do {
+ w = dockWindows.at(index + (++i));
+ } while (i + 1 < (int)dockWindows.count() && (!w || w->isHidden()));
+ if (!w || !w->isResizeEnabled() || i >= (int)dockWindows.count()) {
+ if (orientation() == Qt::Horizontal)
+ return dw->width();
+ return dw->height();
+ }
+ int min = 0;
+ Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(w);
+ if (orientation() == Qt::Horizontal) {
+ w->setFixedExtentWidth(-1);
+ if (!tb)
+ min = qMax(w->minimumSize().width(), w->minimumSizeHint().width());
+ else
+ min = w->sizeHint().width();
+ } else {
+ w->setFixedExtentHeight(-1);
+ if (!tb)
+ min = qMax(w->minimumSize().height(), w->minimumSizeHint().height());
+ else
+ min = w->sizeHint().height();
+ }
+
+ int diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height());
+
+ if ((orientation() == Qt::Horizontal ? w->width() : w->height()) - diff < min)
+ hint = (orientation() == Qt::Horizontal ? dw->width() : dw->height()) + (orientation() == Qt::Horizontal ? w->width() : w->height()) - min;
+
+ diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height());
+ if (orientation() == Qt::Horizontal)
+ w->setFixedExtentWidth(w->width() - diff);
+ else
+ w->setFixedExtentHeight(w->height() - diff);
+ return hint;
+}
+
+void Q3DockArea::setFixedExtent(int d, Q3DockWindow *dw)
+{
+ QList<Q3DockWindow *> lst;
+ for (int i = 0; i < dockWindows.size(); ++i) {
+ Q3DockWindow *w = dockWindows.at(i);
+ if (w->isHidden())
+ continue;
+ if (orientation() == Qt::Horizontal) {
+ if (dw->y() != w->y())
+ continue;
+ } else {
+ if (dw->x() != w->x())
+ continue;
+ }
+ if (orientation() == Qt::Horizontal)
+ d = qMax(d, w->minimumHeight());
+ else
+ d = qMax(d, w->minimumWidth());
+ if (w->isResizeEnabled())
+ lst.append(w);
+ }
+ for (int i = 0; i < lst.size(); ++i) {
+ Q3DockWindow *w = lst.at(i);
+ if (orientation() == Qt::Horizontal)
+ w->setFixedExtentHeight(d);
+ else
+ w->setFixedExtentWidth(d);
+ }
+}
+
+bool Q3DockArea::isLastDockWindow(Q3DockWindow *dw)
+{
+ int i = dockWindows.indexOf(dw);
+ if (i == -1 || i >= (int)dockWindows.count() - 1)
+ return true;
+ Q3DockWindow *w = 0;
+ if ((w = dockWindows.at(++i))) {
+ if (orientation() == Qt::Horizontal && dw->y() < w->y())
+ return true;
+ if (orientation() == Qt::Vertical && dw->x() < w->x())
+ return true;
+ } else {
+ return true;
+ }
+ return false;
+}
+
+#ifndef QT_NO_TEXTSTREAM
+
+/*!
+ \relates Q3DockArea
+
+ Writes the layout of the dock windows in dock area \a dockArea to
+ the text stream \a ts.
+*/
+
+QTextStream &operator<<(QTextStream &ts, const Q3DockArea &dockArea)
+{
+ QString str;
+ QList<Q3DockWindow *> l = dockArea.dockWindowList();
+
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = l.at(i);
+ str += QLatin1Char('[') + QString(dw->windowTitle()) + QLatin1Char(',') + QString::number((int)dw->offset()) +
+ QLatin1Char(',') + QString::number((int)dw->newLine()) + QLatin1Char(',') + QString::number(dw->fixedExtent().width()) +
+ QLatin1Char(',') + QString::number(dw->fixedExtent().height()) + QLatin1Char(',') + QString::number((int)!dw->isHidden()) + QLatin1Char(']');
+ }
+ ts << str << endl;
+
+ return ts;
+}
+
+/*!
+ \relates Q3DockArea
+
+ Reads the layout description of the dock windows in dock area \a
+ dockArea from the text stream \a ts and restores it. The layout
+ description must have been previously written by the operator<<()
+ function.
+*/
+
+QTextStream &operator>>(QTextStream &ts, Q3DockArea &dockArea)
+{
+ QString s = ts.readLine();
+
+ QString name, offset, newLine, width, height, visible;
+
+ enum State { Pre, Name, Offset, NewLine, Width, Height, Visible, Post };
+ int state = Pre;
+ QChar c;
+ QList<Q3DockWindow *> l = dockArea.dockWindowList();
+
+ for (int i = 0; i < s.length(); ++i) {
+ c = s[i];
+ if (state == Pre && c == QLatin1Char('[')) {
+ state++;
+ continue;
+ }
+ if (c == QLatin1Char(',') &&
+ (state == Name || state == Offset || state == NewLine || state == Width || state == Height)) {
+ state++;
+ continue;
+ }
+ if (state == Visible && c == QLatin1Char(']')) {
+ for (int j = 0; j < l.size(); ++j) {
+ Q3DockWindow *dw = l.at(j);
+ if (QString(dw->windowTitle()) == name) {
+ dw->setNewLine((bool)newLine.toInt());
+ dw->setOffset(offset.toInt());
+ dw->setFixedExtentWidth(width.toInt());
+ dw->setFixedExtentHeight(height.toInt());
+ if (!(bool)visible.toInt())
+ dw->hide();
+ else
+ dw->show();
+ break;
+ }
+ }
+
+ name = offset = newLine = width = height = visible = QLatin1String("");
+
+ state = Pre;
+ continue;
+ }
+ if (state == Name)
+ name += c;
+ else if (state == Offset)
+ offset += c;
+ else if (state == NewLine)
+ newLine += c;
+ else if (state == Width)
+ width += c;
+ else if (state == Height)
+ height += c;
+ else if (state == Visible)
+ visible += c;
+ }
+
+ dockArea.QWidget::layout()->invalidate();
+ dockArea.QWidget::layout()->activate();
+ return ts;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_MAINWINDOW
diff --git a/src/qt3support/widgets/q3dockarea.h b/src/qt3support/widgets/q3dockarea.h
new file mode 100644
index 0000000..a784d37
--- /dev/null
+++ b/src/qt3support/widgets/q3dockarea.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DOCKAREA_H
+#define Q3DOCKAREA_H
+
+#include <QtGui/qwidget.h>
+#include <QtCore/qlist.h>
+#include <Qt3Support/q3dockwindow.h>
+#include <QtGui/qlayout.h>
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_MAINWINDOW
+
+class QBoxLayout;
+class Q3DockAreaLayout;
+class QMouseEvent;
+class Q3DockWindowResizeHandle;
+class Q3DockAreaPrivate;
+class QTextStream;
+
+class Q_COMPAT_EXPORT Q3DockAreaLayout : public QLayout
+{
+ Q_OBJECT
+ friend class Q3DockArea;
+
+public:
+ Q3DockAreaLayout(QWidget* parent, Qt::Orientation o, QList<Q3DockWindow *> *wl, int space = -1, int margin = -1, const char *name = 0)
+ : QLayout(parent), orient(o), dirty(true), dockWindows(wl), parentWidget(parent)
+ {
+ if (space != -1)
+ setSpacing(space);
+ if (margin != -1)
+ setMargin(margin);
+ setObjectName(QString::fromAscii(name));
+ init();
+ }
+ ~Q3DockAreaLayout() {}
+
+ void addItem(QLayoutItem *) {}
+ bool hasHeightForWidth() const;
+ int heightForWidth(int) const;
+ int widthForHeight(int) const;
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QLayoutItem *itemAt(int) const;
+ QLayoutItem *takeAt(int);
+ int count() const;
+ Qt::Orientations expandingDirections() const { return Qt::Orientations(0); }
+ void invalidate();
+ Qt::Orientation orientation() const { return orient; }
+ QList<QRect> lineList() const { return lines; }
+ QList<Q3DockWindow *> lineStarts() const { return ls; }
+
+protected:
+ void setGeometry(const QRect&);
+
+private:
+ Q_DISABLE_COPY(Q3DockAreaLayout)
+
+ void init();
+ int layoutItems(const QRect&, bool testonly = false);
+ Qt::Orientation orient;
+ bool dirty;
+ int cached_width, cached_height;
+ int cached_hfw, cached_wfh;
+ QList<Q3DockWindow *> *dockWindows;
+ QWidget *parentWidget;
+ QList<QRect> lines;
+ QList<Q3DockWindow *> ls;
+};
+
+class Q_COMPAT_EXPORT Q3DockArea : public QWidget
+{
+ Q_OBJECT
+ Q_ENUMS(HandlePosition)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation)
+ Q_PROPERTY(int count READ count)
+ Q_PROPERTY(bool empty READ isEmpty)
+ Q_PROPERTY(HandlePosition handlePosition READ handlePosition)
+
+ friend class Q3DockWindow;
+ friend class Q3DockWindowResizeHandle;
+ friend class Q3DockAreaLayout;
+
+public:
+ enum HandlePosition { Normal, Reverse };
+
+ Q3DockArea(Qt::Orientation o, HandlePosition h = Normal, QWidget* parent=0, const char* name=0);
+ ~Q3DockArea();
+
+ void moveDockWindow(Q3DockWindow *w, const QPoint &globalPos, const QRect &rect, bool swap);
+ void removeDockWindow(Q3DockWindow *w, bool makeFloating, bool swap, bool fixNewLines = true);
+ void moveDockWindow(Q3DockWindow *w, int index = -1);
+ bool hasDockWindow(Q3DockWindow *w, int *index = 0);
+
+ void invalidNextOffset(Q3DockWindow *dw);
+
+ Qt::Orientation orientation() const { return orient; }
+ HandlePosition handlePosition() const { return hPos; }
+
+ bool eventFilter(QObject *, QEvent *);
+ bool isEmpty() const;
+ int count() const;
+ QList<Q3DockWindow *> dockWindowList() const;
+
+ bool isDockWindowAccepted(Q3DockWindow *dw);
+ void setAcceptDockWindow(Q3DockWindow *dw, bool accept);
+
+public Q_SLOTS:
+ void lineUp(bool keepNewLines);
+
+private:
+ struct DockWindowData
+ {
+ int index;
+ int offset;
+ int line;
+ QSize fixedExtent;
+ QPointer<Q3DockArea> area;
+ };
+
+ int findDockWindow(Q3DockWindow *w);
+ int lineOf(int index);
+ DockWindowData *dockWindowData(Q3DockWindow *w);
+ void dockWindow(Q3DockWindow *dockWindow, DockWindowData *data);
+ void updateLayout();
+ void invalidateFixedSizes();
+ int maxSpace(int hint, Q3DockWindow *dw);
+ void setFixedExtent(int d, Q3DockWindow *dw);
+ bool isLastDockWindow(Q3DockWindow *dw);
+
+private:
+ Q_DISABLE_COPY(Q3DockArea)
+
+ Qt::Orientation orient;
+ QList<Q3DockWindow *> dockWindows;
+ Q3DockAreaLayout *layout;
+ HandlePosition hPos;
+ QList<Q3DockWindow *> forbiddenWidgets;
+ Q3DockAreaPrivate *d;
+};
+
+#ifndef QT_NO_TEXTSTREAM
+Q_COMPAT_EXPORT QTextStream &operator<<(QTextStream &, const Q3DockArea &);
+Q_COMPAT_EXPORT QTextStream &operator>>(QTextStream &, Q3DockArea &);
+#endif
+
+#endif // QT_NO_MAINWINDOW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DOCKAREA_H
diff --git a/src/qt3support/widgets/q3dockwindow.cpp b/src/qt3support/widgets/q3dockwindow.cpp
new file mode 100644
index 0000000..c6e491c
--- /dev/null
+++ b/src/qt3support/widgets/q3dockwindow.cpp
@@ -0,0 +1,2115 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dockwindow.h"
+
+#ifndef QT_NO_MAINWINDOW
+#include "qapplication.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "q3dockarea.h"
+#include "qevent.h"
+#include "qlayout.h"
+#include "q3mainwindow.h"
+#include "qpainter.h"
+#include "qpointer.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "q3toolbar.h"
+#include "qtoolbutton.h"
+#include "qtooltip.h"
+#include <private/q3titlebar_p.h>
+#include <private/qwidgetresizehandler_p.h>
+#include <qrubberband.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_MAC
+static bool default_opaque = true;
+#else
+static bool default_opaque = false;
+#endif
+
+class Q3DockWindowPrivate
+{
+};
+
+class Q3DockWindowResizeHandle : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Q3DockWindowResizeHandle(Qt::Orientation o, QWidget *parent, Q3DockWindow *w, const char* /*name*/=0);
+ void setOrientation(Qt::Orientation o);
+ Qt::Orientation orientation() const { return orient; }
+
+ QSize sizeHint() const;
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ bool event(QEvent *event);
+
+private:
+ void startLineDraw();
+ void endLineDraw();
+ void drawLine(const QPoint &globalPos);
+
+private:
+ Qt::Orientation orient;
+ bool mousePressed;
+ QRubberBand *rubberBand;
+ QPoint lastPos, firstPos;
+ Q3DockWindow *dockWindow;
+ bool mouseOver;
+};
+
+Q3DockWindowResizeHandle::Q3DockWindowResizeHandle(Qt::Orientation o, QWidget *parent,
+ Q3DockWindow *w, const char *)
+ : QWidget(parent, "qt_dockwidget_internal"), mousePressed(false), rubberBand(0), dockWindow(w),
+ mouseOver(false)
+{
+ setOrientation(o);
+}
+
+QSize Q3DockWindowResizeHandle::sizeHint() const
+{
+ QStyleOptionQ3DockWindow opt;
+ opt.init(this);
+ if (!dockWindow->area() || dockWindow->area()->orientation() == Qt::Horizontal)
+ opt.state |= QStyle::State_Horizontal;
+
+ opt.rect = rect();
+ opt.docked = dockWindow->area();
+ opt.closeEnabled = dockWindow->isCloseEnabled();
+ int sw = 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, &opt, this) / 3;
+ return (style()->sizeFromContents(QStyle::CT_Q3DockWindow, &opt, QSize(sw, sw), this).expandedTo(QApplication::globalStrut()));
+}
+
+void Q3DockWindowResizeHandle::setOrientation(Qt::Orientation o)
+{
+ orient = o;
+ if (o == Qt::Horizontal) {
+#ifndef QT_NO_CURSOR
+ setCursor(Qt::splitVCursor);
+#endif
+ setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+ } else {
+#ifndef QT_NO_CURSOR
+ setCursor(Qt::splitHCursor);
+#endif
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding));
+ }
+}
+
+void Q3DockWindowResizeHandle::mousePressEvent(QMouseEvent *e)
+{
+ e->ignore();
+ if (e->button() != Qt::LeftButton)
+ return;
+ e->accept();
+ mousePressed = true;
+ if (!dockWindow->opaqueMoving())
+ startLineDraw();
+ lastPos = firstPos = e->globalPos();
+ if (!dockWindow->opaqueMoving())
+ drawLine(e->globalPos());
+}
+
+void Q3DockWindowResizeHandle::mouseMoveEvent(QMouseEvent *e)
+{
+ if (!mousePressed)
+ return;
+ if (!dockWindow->opaqueMoving()) {
+ if (orientation() != dockWindow->area()->orientation()) {
+ if (orientation() == Qt::Horizontal) {
+ int minpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).y();
+ int maxpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).y() + dockWindow->area()->height();
+ if (e->globalPos().y() < minpos || e->globalPos().y() > maxpos)
+ return;
+ } else {
+ int minpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).x();
+ int maxpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).x() + dockWindow->area()->width();
+ if (e->globalPos().x() < minpos || e->globalPos().x() > maxpos)
+ return;
+ }
+ } else {
+ QWidget *w = dockWindow->area()->window();
+ if (w) {
+ if (orientation() == Qt::Horizontal) {
+ int minpos = w->mapToGlobal(QPoint(0, 0)).y();
+ int maxpos = w->mapToGlobal(QPoint(0, 0)).y() + w->height();
+ if (e->globalPos().y() < minpos || e->globalPos().y() > maxpos)
+ return;
+ } else {
+ int minpos = w->mapToGlobal(QPoint(0, 0)).x();
+ int maxpos = w->mapToGlobal(QPoint(0, 0)).x() + w->width();
+ if (e->globalPos().x() < minpos || e->globalPos().x() > maxpos)
+ return;
+ }
+ }
+ }
+ }
+
+ if (!dockWindow->opaqueMoving())
+ drawLine(lastPos);
+ lastPos = e->globalPos();
+ if (dockWindow->opaqueMoving()) {
+ mouseReleaseEvent(e);
+ mousePressed = true;
+ firstPos = e->globalPos();
+ }
+ if (!dockWindow->opaqueMoving())
+ drawLine(e->globalPos());
+}
+
+void Q3DockWindowResizeHandle::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (mousePressed) {
+ if (!dockWindow->opaqueMoving()) {
+ drawLine(lastPos);
+ endLineDraw();
+ }
+ if (orientation() != dockWindow->area()->orientation())
+ dockWindow->area()->invalidNextOffset(dockWindow);
+ if (orientation() == Qt::Horizontal) {
+ int dy;
+ if (dockWindow->area()->handlePosition() == Q3DockArea::Normal || orientation() != dockWindow->area()->orientation())
+ dy = e->globalPos().y() - firstPos.y();
+ else
+ dy = firstPos.y() - e->globalPos().y();
+ int d = dockWindow->height() + dy;
+ if (orientation() != dockWindow->area()->orientation()) {
+ dockWindow->setFixedExtentHeight(-1);
+ d = qMax(d, dockWindow->minimumHeight());
+ int ms = dockWindow->area()->maxSpace(d, dockWindow);
+ d = qMin(d, ms);
+ dockWindow->setFixedExtentHeight(d);
+ } else {
+ dockWindow->area()->setFixedExtent(d, dockWindow);
+ }
+ } else {
+ int dx;
+ if (dockWindow->area()->handlePosition() == Q3DockArea::Normal || orientation() != dockWindow->area()->orientation())
+ dx = e->globalPos().x() - firstPos.x();
+ else
+ dx = firstPos.x() - e->globalPos().x();
+ int d = dockWindow->width() + dx;
+ if (orientation() != dockWindow->area()->orientation()) {
+ dockWindow->setFixedExtentWidth(-1);
+ d = qMax(d, dockWindow->minimumWidth());
+ int ms = dockWindow->area()->maxSpace(d, dockWindow);
+ d = qMin(d, ms);
+ dockWindow->setFixedExtentWidth(d);
+ } else {
+ dockWindow->area()->setFixedExtent(d, dockWindow);
+ }
+ }
+ }
+
+ QApplication::postEvent(dockWindow->area(), new QEvent(QEvent::LayoutHint));
+ mousePressed = false;
+}
+
+bool Q3DockWindowResizeHandle::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::HoverEnter:
+ if (!mouseOver) {
+ mouseOver = true;
+ update();
+ }
+ break;
+ case QEvent::HoverLeave:
+ if (mouseOver) {
+ mouseOver = false;
+ update();
+ }
+ break;
+ default:
+ break;
+ }
+ return QWidget::event(event);
+}
+
+void Q3DockWindowResizeHandle::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOption opt(0);
+ opt.init(this);
+ if (orientation() == Qt::Horizontal)
+ opt.state |= QStyle::State_Horizontal;
+ style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, &p, this);
+}
+
+void Q3DockWindowResizeHandle::startLineDraw()
+{
+ if (rubberBand)
+ endLineDraw();
+ rubberBand = new QRubberBand(QRubberBand::Line);
+ rubberBand->setGeometry(-1, -1, 1, 1);
+ rubberBand->show();
+}
+
+void Q3DockWindowResizeHandle::endLineDraw()
+{
+ delete rubberBand;
+ rubberBand = 0;
+}
+
+void Q3DockWindowResizeHandle::drawLine(const QPoint &globalPos)
+{
+ QPoint start = mapToGlobal(QPoint(0, 0));
+ QPoint starta = dockWindow->area()->mapToGlobal(QPoint(0, 0));
+ QPoint end = globalPos;
+ if (orientation() == Qt::Horizontal) {
+ if (orientation() == dockWindow->orientation())
+ rubberBand->setGeometry(starta.x(), end.y(), dockWindow->area()->width(), height());
+ else
+ rubberBand->setGeometry(start.x(), end.y(), width(), height());
+ } else {
+ if (orientation() == dockWindow->orientation())
+ rubberBand->setGeometry(end.x(), starta.y(), width(), dockWindow->area()->height());
+ else
+ rubberBand->setGeometry(end.x(), start.y(), width(), height());
+ }
+}
+
+static QPoint realWidgetPos(Q3DockWindow *w)
+{
+ if (!w->parentWidget() || w->place() == Q3DockWindow::OutsideDock)
+ return w->pos();
+ return w->parentWidget()->mapToGlobal(w->geometry().topLeft());
+}
+
+class Q3DockWindowHandle : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QString windowTitle READ windowTitle)
+ friend class Q3DockWindow;
+ friend class Q3DockWindowTitleBar;
+
+public:
+ Q3DockWindowHandle(Q3DockWindow *dw);
+ void updateGui();
+
+ QSize minimumSizeHint() const;
+ QSize minimumSize() const { return minimumSizeHint(); }
+ QSize sizeHint() const { return minimumSize(); }
+ void setOpaqueMoving(bool b) { opaque = b; }
+
+ QString windowTitle() const { return dockWindow->windowTitle(); }
+
+signals:
+ void doubleClicked();
+
+protected:
+ void paintEvent(QPaintEvent *e);
+ void resizeEvent(QResizeEvent *e);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void mouseDoubleClickEvent(QMouseEvent *e);
+ void keyPressEvent(QKeyEvent *e);
+ void keyReleaseEvent(QKeyEvent *e);
+ void changeEvent(QEvent *);
+
+private slots:
+ void minimize();
+
+private:
+ Q3DockWindow *dockWindow;
+ QPoint offset;
+ QToolButton *closeButton;
+ QTimer *timer;
+ uint opaque : 1;
+ uint mousePressed : 1;
+ uint hadDblClick : 1;
+ uint ctrlDown : 1;
+ QPointer<QWidget> oldFocus;
+};
+
+class Q3DockWindowTitleBar : public Q3TitleBar
+{
+ Q_OBJECT
+ friend class Q3DockWindow;
+ friend class Q3DockWindowHandle;
+
+public:
+ Q3DockWindowTitleBar(Q3DockWindow *dw);
+ void updateGui();
+ void setOpaqueMoving(bool b) { opaque = b; }
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void mouseDoubleClickEvent(QMouseEvent *e);
+ void keyPressEvent(QKeyEvent *e);
+ void keyReleaseEvent(QKeyEvent *e);
+
+private:
+ Q3DockWindow *dockWindow;
+ QPoint offset;
+ uint mousePressed : 1;
+ uint hadDblClick : 1;
+ uint opaque : 1;
+ uint ctrlDown : 1;
+ QPointer<QWidget> oldFocus;
+
+};
+
+Q3DockWindowHandle::Q3DockWindowHandle(Q3DockWindow *dw)
+ : QWidget(dw, "qt_dockwidget_internal"), dockWindow(dw),
+ closeButton(0), opaque(default_opaque), mousePressed(false)
+{
+ ctrlDown = false;
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(minimize()));
+#if defined(Q_WS_WIN) && !defined(QT_NO_CURSOR)
+ setCursor(Qt::SizeAllCursor);
+#endif
+}
+
+void Q3DockWindowHandle::paintEvent(QPaintEvent *e)
+{
+ if (!dockWindow->dockArea && !opaque)
+ return;
+ QPainter p(this);
+ QStyleOptionQ3DockWindow opt;
+ opt.init(this);
+ if (!dockWindow->area() || dockWindow->area()->orientation() == Qt::Horizontal)
+ opt.state |= QStyle::State_Horizontal;
+
+ opt.rect = rect();
+ opt.docked = dockWindow->area();
+ opt.closeEnabled = dockWindow->isCloseEnabled();
+ opt.rect = QStyle::visualRect(opt.direction, opt.rect,
+ style()->subElementRect(QStyle::SE_Q3DockWindowHandleRect, &opt, this));
+ style()->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this);
+ QWidget::paintEvent(e);
+}
+
+void Q3DockWindowHandle::keyPressEvent(QKeyEvent *e)
+{
+ if (!mousePressed)
+ return;
+ if (e->key() == Qt::Key_Control) {
+ ctrlDown = true;
+ dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque);
+ }
+}
+
+void Q3DockWindowHandle::keyReleaseEvent(QKeyEvent *e)
+{
+ if (!mousePressed)
+ return;
+ if (e->key() == Qt::Key_Control) {
+ ctrlDown = false;
+ dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque);
+ }
+}
+
+void Q3DockWindowHandle::mousePressEvent(QMouseEvent *e)
+{
+ if (!dockWindow->dockArea)
+ return;
+ ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton;
+ oldFocus = qApp->focusWidget();
+ setFocus();
+ e->ignore();
+ if (e->button() != Qt::LeftButton)
+ return;
+ e->accept();
+ hadDblClick = false;
+ mousePressed = true;
+ offset = e->pos();
+ dockWindow->startRectDraw(mapToGlobal(e->pos()), !opaque);
+ if (!opaque)
+ qApp->installEventFilter(dockWindow);
+}
+
+void Q3DockWindowHandle::mouseMoveEvent(QMouseEvent *e)
+{
+ if (!mousePressed || e->pos() == offset)
+ return;
+ ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton;
+ dockWindow->handleMove(e->pos() - offset, e->globalPos(), !opaque);
+ if (opaque)
+ dockWindow->updatePosition(e->globalPos());
+}
+
+void Q3DockWindowHandle::mouseReleaseEvent(QMouseEvent *e)
+{
+ ctrlDown = false;
+ qApp->removeEventFilter(dockWindow);
+ if (oldFocus)
+ oldFocus->setFocus();
+ if (!mousePressed)
+ return;
+ dockWindow->endRectDraw(!opaque);
+ mousePressed = false;
+#ifdef Q_WS_MAC
+ releaseMouse();
+#endif
+ if (!hadDblClick && offset == e->pos()) {
+ timer->start(QApplication::doubleClickInterval(), true);
+ } else if (!hadDblClick) {
+ dockWindow->updatePosition(e->globalPos());
+ }
+ if (opaque)
+ dockWindow->titleBar->mousePressed = false;
+ if (dockWindow->parentWidget())
+ QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint));
+}
+
+void Q3DockWindowHandle::minimize()
+{
+ if (!dockWindow->area())
+ return;
+
+ Q3MainWindow *mw = qobject_cast<Q3MainWindow*>(dockWindow->area()->parentWidget());
+ if (mw && mw->isDockEnabled(dockWindow, Qt::DockMinimized))
+ mw->moveDockWindow(dockWindow, Qt::DockMinimized);
+}
+
+void Q3DockWindowHandle::resizeEvent(QResizeEvent *)
+{
+ updateGui();
+}
+
+void Q3DockWindowHandle::updateGui()
+{
+ updateGeometry();
+
+ if (!closeButton) {
+ closeButton = new QToolButton(this, "qt_close_button1");
+#ifndef QT_NO_CURSOR
+ closeButton->setCursor(Qt::ArrowCursor);
+#endif
+ QStyleOption opt(0);
+ opt.init(closeButton);
+ closeButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton, &opt,
+ closeButton));
+ closeButton->setFixedSize(12, 12);
+ connect(closeButton, SIGNAL(clicked()),
+ dockWindow, SLOT(hide()));
+ }
+
+ if (dockWindow->isCloseEnabled() && dockWindow->area())
+ closeButton->show();
+ else
+ closeButton->hide();
+
+ if (!dockWindow->area())
+ return;
+
+ if (dockWindow->area()->orientation() == Qt::Horizontal) {
+ int off = (width() - closeButton->width() - 1) / 2;
+ closeButton->move(off, 2);
+ } else {
+ int off = (height() - closeButton->height() - 1) / 2;
+ int x = QApplication::reverseLayout() ? 2 : width() - closeButton->width() - 2;
+ closeButton->move(x, off);
+ }
+}
+
+void Q3DockWindowHandle::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange) {
+ if (closeButton) {
+ QStyleOption opt(0);
+ opt.init(closeButton);
+ closeButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton,
+ &opt, closeButton));
+ }
+ }
+ QWidget::changeEvent(ev);
+}
+
+QSize Q3DockWindowHandle::minimumSizeHint() const
+{
+ if (!dockWindow->dockArea)
+ return QSize(0, 0);
+ int wh = dockWindow->isCloseEnabled() ? 17 : style()->pixelMetric(QStyle::PM_ToolBarHandleExtent, 0, this);
+ if (dockWindow->orientation() == Qt::Horizontal)
+ return QSize(wh, 0);
+ return QSize(0, wh);
+}
+
+void Q3DockWindowHandle::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ e->ignore();
+ if (e->button() != Qt::LeftButton)
+ return;
+ e->accept();
+ timer->stop();
+ emit doubleClicked();
+ hadDblClick = true;
+ if (dockWindow->parentWidget())
+ QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint));
+}
+
+Q3DockWindowTitleBar::Q3DockWindowTitleBar(Q3DockWindow *dw)
+ : Q3TitleBar(0, dw), dockWindow(dw),
+ mousePressed(false), hadDblClick(false), opaque(default_opaque)
+{
+ setObjectName(QLatin1String("qt_dockwidget_internal"));
+ ctrlDown = false;
+ setMouseTracking(true);
+ QStyleOptionTitleBar opt = getStyleOption();
+ setFixedHeight(style()->pixelMetric(QStyle::PM_TitleBarHeight, &opt, this));
+ connect(this, SIGNAL(doClose()), dockWindow, SLOT(hide()));
+}
+
+void Q3DockWindowTitleBar::keyPressEvent(QKeyEvent *e)
+{
+ if (!mousePressed)
+ return;
+ if (e->key() == Qt::Key_Control) {
+ ctrlDown = true;
+ dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque);
+ }
+}
+
+void Q3DockWindowTitleBar::keyReleaseEvent(QKeyEvent *e)
+{
+ if (!mousePressed)
+ return;
+ if (e->key() == Qt::Key_Control) {
+ ctrlDown = false;
+ dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque);
+ }
+}
+
+void Q3DockWindowTitleBar::mousePressEvent(QMouseEvent *e)
+{
+ QStyleOptionTitleBar opt;
+ opt.init(this);
+ opt.subControls = QStyle::SC_All;
+ opt.activeSubControls = QStyle::SC_None;
+ opt.text = windowTitle();
+ //################
+ QIcon icon = windowIcon();
+ QSize s = icon.actualSize(QSize(64, 64));
+ opt.icon = icon.pixmap(s);
+ opt.titleBarState = window() ? window()->windowState() : static_cast<Qt::WindowStates>(Qt::WindowNoState);
+ opt.titleBarFlags = fakeWindowFlags();
+ QStyle::SubControl tbctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt,
+ e->pos(), this);
+
+ if (tbctrl < QStyle::SC_TitleBarLabel && tbctrl != QStyle::SC_None) {
+ Q3TitleBar::mousePressEvent(e);
+ return;
+ }
+
+ ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton;
+ oldFocus = qApp->focusWidget();
+// setFocus activates the window, which deactivates the main window
+// not what we want, and not required anyway on Windows
+#ifndef Q_WS_WIN
+ setFocus();
+#endif
+
+ e->ignore();
+ if (e->button() != Qt::LeftButton)
+ return;
+ if (e->y() < 3 && dockWindow->isResizeEnabled())
+ return;
+
+ e->accept();
+ bool oldPressed = mousePressed;
+ mousePressed = true;
+ hadDblClick = false;
+ offset = e->pos();
+ dockWindow->startRectDraw(mapToGlobal(e->pos()), !opaque);
+// grabMouse resets the Windows mouse press count, so we never receive a double click on Windows
+// not required on Windows, and did work on X11, too, but no problem there in the first place
+#ifndef Q_WS_WIN
+ if(!oldPressed && dockWindow->opaqueMoving())
+ grabMouse();
+#else
+ Q_UNUSED(oldPressed);
+#endif
+}
+
+void Q3DockWindowTitleBar::mouseMoveEvent(QMouseEvent *e)
+{
+ if (!mousePressed) {
+ Q3TitleBar::mouseMoveEvent(e);
+ return;
+ }
+
+ ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton;
+ e->accept();
+ dockWindow->handleMove(e->pos() - offset, e->globalPos(), !opaque);
+}
+
+void Q3DockWindowTitleBar::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (!mousePressed) {
+ Q3TitleBar::mouseReleaseEvent(e);
+ return;
+ }
+
+ ctrlDown = false;
+ qApp->removeEventFilter(dockWindow);
+ if (oldFocus)
+ oldFocus->setFocus();
+
+ if (dockWindow->place() == Q3DockWindow::OutsideDock)
+ dockWindow->raise();
+
+ if(dockWindow->opaqueMoving())
+ releaseMouse();
+ if (!mousePressed)
+ return;
+ dockWindow->endRectDraw(!opaque);
+ mousePressed = false;
+ if (!hadDblClick)
+ dockWindow->updatePosition(e->globalPos());
+ if (opaque) {
+ dockWindow->horHandle->mousePressed = false;
+ dockWindow->verHandle->mousePressed = false;
+ }
+ if (dockWindow->parentWidget())
+ QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint));
+}
+
+void Q3DockWindowTitleBar::resizeEvent(QResizeEvent *e)
+{
+ updateGui();
+ Q3TitleBar::resizeEvent(e);
+}
+
+void Q3DockWindowTitleBar::updateGui()
+{
+ if (dockWindow->isCloseEnabled()) {
+ setFakeWindowFlags(fakeWindowFlags() | Qt::WStyle_SysMenu);
+ } else {
+ setFakeWindowFlags(fakeWindowFlags() & ~Qt::WStyle_SysMenu);
+ }
+}
+
+void Q3DockWindowTitleBar::mouseDoubleClickEvent(QMouseEvent *)
+{
+ emit doubleClicked();
+ hadDblClick = true;
+ if (dockWindow->parentWidget())
+ QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint));
+}
+
+/*!
+ \class Q3DockWindow
+ \brief The Q3DockWindow class provides a widget which can be docked
+ inside a Q3DockArea or floated as a top level window on the
+ desktop.
+
+ \compat
+
+ This class handles moving, resizing, docking and undocking dock
+ windows. Q3ToolBar is a subclass of Q3DockWindow so the
+ functionality provided for dock windows is available with the same
+ API for toolbars.
+
+ \img qmainwindow-qdockareas.png Q3DockWindows in a Q3DockArea
+ \caption Two Q3DockWindows (\l{Q3ToolBar}s) in a \l Q3DockArea
+
+ \img qdockwindow.png A Q3DockWindow
+ \caption A Floating Q3DockWindow
+
+ If the user drags the dock window into the dock area the dock
+ window will be docked. If the user drags the dock area outside any
+ dock areas the dock window will be undocked (floated) and will
+ become a top level window. Double clicking a floating dock
+ window's title bar will dock the dock window to the last dock area
+ it was docked in. Double clicking a docked dock window's handle
+ will undock (float) the dock window.
+ \omit
+ Single clicking a docked dock window's handle will minimize the
+ dock window (only its handle will appear, below the menu bar).
+ Single clicking the minimized handle will restore the dock window
+ to the last dock area that it was docked in.
+ \endomit
+ If the user clicks the close button (which appears on floating
+ dock windows by default) the dock window will disappear. You can
+ control whether or not a dock window has a close button with
+ setCloseMode().
+
+ Q3MainWindow provides four dock areas (top, left, right and bottom)
+ which can be used by dock windows. For many applications using the
+ dock areas provided by Q3MainWindow is sufficient. (See the \l
+ Q3DockArea documentation if you want to create your own dock
+ areas.) In Q3MainWindow a right-click popup menu (the dock window
+ menu) is available which lists dock windows and can be used to
+ show or hide them. (The popup menu only lists dock windows that
+ have a \link QWidget::setWindowTitle() caption\endlink.)
+
+ When you construct a dock window you \e must pass it a Q3DockArea
+ or a Q3MainWindow as its parent if you want it docked. Pass 0 for
+ the parent if you want it floated.
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockwindow.cpp 0
+
+ In the example above we create a new Q3ToolBar in the constructor
+ of a Q3MainWindow subclass (so that the \e this pointer points to
+ the Q3MainWindow). By default the toolbar will be added to the \c
+ Top dock area, but we've moved it to the \c Left dock area.
+
+ A dock window is often used to contain a single widget. In these
+ cases the widget can be set by calling setWidget(). If you're
+ constructing a dock window that contains multiple widgets, e.g. a
+ toolbar, arrange the widgets within a box layout inside the dock
+ window. To do this use the boxLayout() function to get a pointer
+ to the dock window's box layout, then add widgets to the layout
+ using the box layout's QBoxLayout::addWidget() function. The dock
+ window will dynamically set the orientation of the layout to be
+ vertical or horizontal as necessary, although you can control this
+ yourself with setOrientation().
+
+ Although a common use of dock windows is for toolbars, they can be
+ used with any widgets. When using larger
+ widgets it may make sense for the dock window to be resizable by
+ calling setResizeEnabled(). Resizable dock windows are given
+ splitter-like handles to allow the user to resize them within
+ their dock area. When resizable dock windows are undocked they
+ become top level windows and can be resized like any other top
+ level windows, e.g. by dragging a corner or edge.
+
+ Qt::Dock windows can be docked and undocked using dock() and undock().
+ A dock window's orientation can be set with setOrientation(). You
+ can also use Q3DockArea::moveDockWindow(). If you're using a
+ Q3MainWindow, Q3MainWindow::moveDockWindow() and
+ Q3MainWindow::removeDockWindow() are available.
+
+ A dock window can have some preferred settings, for example, you
+ can set a preferred offset from the left edge (or top edge for
+ vertical dock areas) of the dock area using setOffset(). If you'd
+ prefer a dock window to start on a new line when it is docked use
+ setNewLine(). The setFixedExtentWidth() and setFixedExtentHeight()
+ functions can be used to define the dock window's preferred size,
+ and the setHorizontallyStretchable() and setVerticallyStretchable()
+ functions set whether the dock window can be stretched or not.
+ Dock windows can be moved by default, but this can be changed with
+ setMovingEnabled(). When a dock window is moved it is shown as a
+ rectangular outline, but it can be shown normally using
+ setOpaqueMoving().
+
+ When a dock window's visibility changes, i.e. it is shown or
+ hidden, the visibilityChanged() signal is emitted. When a dock
+ window is docked, undocked or moved inside the dock area the
+ placeChanged() signal is emitted.
+*/
+
+/*!
+ \enum Q3DockWindow::Place
+
+ This enum specifies the possible locations for a Q3DockWindow:
+
+ \value InDock Inside a Q3DockArea.
+ \value OutsideDock Floating as a top level window on the desktop.
+*/
+
+/*!
+ \enum Q3DockWindow::CloseMode
+
+ This enum type specifies when (if ever) a dock window has a close
+ button.
+
+ \value Never The dock window never has a close button and cannot
+ be closed by the user.
+ \value Docked The dock window has a close button only when
+ docked.
+ \value Undocked The dock window has a close button only when
+ floating.
+ \value Always The dock window always has a close button.
+ \omit
+ Note that dock windows can always be minimized if the user clicks
+ their dock window handle when they are docked.
+ \endomit
+*/
+
+/*!
+ \fn void Q3DockWindow::setHorizontalStretchable(bool b)
+
+ If \a b is true the dock window is set to be horizontally
+ stretchable.
+*/
+/*!
+ \fn void Q3DockWindow::setVerticalStretchable(bool b)
+
+ If \a b is true the dock window is set to be vertically
+ stretchable.
+*/
+/*!
+ \fn bool Q3DockWindow::isHorizontalStretchable() const
+
+ Returns true if the dock window can be stretched horizontally;
+ otherwise returns false.
+*/
+/*!
+ \fn bool Q3DockWindow::isVerticalStretchable() const
+
+ Returns true if the dock window can be stretched vertically;
+ otherwise returns false.
+*/
+/*!
+ \fn void Q3DockWindow::orientationChanged(Qt::Orientation o)
+
+ This signal is emitted when the orientation of the dock window is
+ changed. The new orientation is \a o.
+*/
+
+/*!
+ \fn void Q3DockWindow::placeChanged(Q3DockWindow::Place p)
+
+ This signal is emitted when the dock window is docked (\a p is \c
+ InDock), undocked (\a p is \c OutsideDock) or moved inside the
+ the dock area.
+
+ \sa Q3DockArea::moveDockWindow(), Q3DockArea::removeDockWindow(),
+ Q3MainWindow::moveDockWindow(), Q3MainWindow::removeDockWindow()
+*/
+
+/*!
+ \fn void Q3DockWindow::visibilityChanged(bool visible)
+
+ This signal is emitted when the visibility of the dock window
+ relatively to its dock area is changed. If \a visible is true, the
+ Q3DockWindow is now visible to the dock area, otherwise it has been
+ hidden.
+
+ A dock window can be hidden if it has a close button which the
+ user has clicked. In the case of a Q3MainWindow a dock window can
+ have its visibility changed (hidden or shown) by clicking its name
+ in the dock window menu that lists the Q3MainWindow's dock windows.
+*/
+
+/*!
+ \fn Q3DockArea *Q3DockWindow::area() const
+
+ Returns the dock area in which this dock window is docked, or 0 if
+ the dock window is floating.
+*/
+
+/*!
+ \property Q3DockWindow::place
+ \brief the location where the dock window is placed
+
+ This is either \c InDock or \c OutsideDock.
+
+ \sa Q3DockArea::moveDockWindow(), Q3DockArea::removeDockWindow(),
+ Q3MainWindow::moveDockWindow(), Q3MainWindow::removeDockWindow()
+*/
+
+/*!
+ Constructs a Q3DockWindow with parent \a parent, called \a name and
+ with widget flags \a f.
+*/
+
+Q3DockWindow::Q3DockWindow(QWidget* parent, const char* name, Qt::WindowFlags f)
+ : Q3Frame(parent, name, f | Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder)
+{
+ curPlace = InDock;
+ isToolbar = false;
+ init();
+}
+
+/*!
+ Constructs a Q3DockWindow with parent \a parent, called \a name and
+ with widget flags \a f.
+
+ If \a p is \c InDock, the dock window is docked into a dock area
+ and \a parent \e must be a Q3DockArea or a Q3MainWindow. If the \a
+ parent is a Q3MainWindow the dock window will be docked in the main
+ window's \c Top dock area.
+
+ If \a p is \c OutsideDock, the dock window is created as a floating
+ window.
+
+ We recommend creating the dock area \c InDock with a Q3MainWindow
+ as parent then calling Q3MainWindow::moveDockWindow() to move the
+ dock window where you want it.
+*/
+
+Q3DockWindow::Q3DockWindow(Place p, QWidget *parent, const char *name, Qt::WindowFlags f)
+ : Q3Frame(parent, name, f | Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder)
+{
+ curPlace = p;
+ isToolbar = false;
+ init();
+}
+
+/*! \internal
+*/
+
+Q3DockWindow::Q3DockWindow(Place p, QWidget *parent, const char *name, Qt::WindowFlags f, bool toolbar)
+ : Q3Frame(parent, name, f | Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder)
+{
+ curPlace = p;
+ isToolbar = toolbar;
+ init();
+}
+
+class Q3DockWindowGridLayout : public QGridLayout
+{
+public:
+ Q3DockWindowGridLayout(QWidget *parent, int nRows, int nCols)
+ : QGridLayout(parent, nRows, nCols) {};
+
+ Qt::Orientations expandingDirections() const
+ {
+ return 0;
+ }
+};
+
+void Q3DockWindow::init()
+{
+ wid = 0;
+ rubberBand = 0;
+ dockArea = 0;
+ tmpDockArea = 0;
+ resizeEnabled = false;
+ moveEnabled = true;
+ nl = false;
+ opaque = default_opaque;
+ cMode = Never;
+ offs = 0;
+ fExtent = QSize(-1, -1);
+ dockWindowData = 0;
+ lastPos = QPoint(-1, -1);
+ lastSize = QSize(-1, -1);
+ stretchable[Qt::Horizontal] = false;
+ stretchable[Qt::Vertical] = false;
+
+ widgetResizeHandler = new QWidgetResizeHandler(this);
+ widgetResizeHandler->setMovingEnabled(false);
+
+ titleBar = new Q3DockWindowTitleBar(this);
+ verHandle = new Q3DockWindowHandle(this);
+ verHandle->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
+ horHandle = new Q3DockWindowHandle(this);
+ horHandle->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+
+ vHandleLeft = new Q3DockWindowResizeHandle(Qt::Vertical, this, this, "vert. handle");
+ vHandleRight = new Q3DockWindowResizeHandle(Qt::Vertical, this, this, "vert. handle");
+ hHandleTop = new Q3DockWindowResizeHandle(Qt::Horizontal, this, this, "horz. handle");
+ hHandleBottom = new Q3DockWindowResizeHandle(Qt::Horizontal, this, this, "horz. handle");
+
+ // Creating inner layout
+ hbox = new QVBoxLayout();
+ vbox = new QHBoxLayout();
+ childBox = new QBoxLayout(QBoxLayout::LeftToRight);
+ vbox->addSpacing(2);
+ vbox->addWidget(verHandle);
+ vbox->addStretch(0);
+ vbox->addLayout(childBox, 1);
+ vbox->addStretch(0);
+
+ hbox->setResizeMode(QLayout::FreeResize);
+ hbox->setMargin(isResizeEnabled() || curPlace == OutsideDock ? 2 : 0);
+ hbox->setSpacing(1);
+ hbox->addWidget(titleBar);
+ hbox->addWidget(horHandle);
+ hbox->addLayout(vbox);
+
+ // Set up the initial handle layout for Qt::Vertical
+ // Handle layout will change on calls to setOrienation()
+ QGridLayout *glayout = new Q3DockWindowGridLayout(this, 3, 3);
+ glayout->setResizeMode(QLayout::Minimum);
+ glayout->addMultiCellWidget(hHandleTop, 0, 0, 1, 1);
+ glayout->addMultiCellWidget(hHandleBottom, 2, 2, 1, 1);
+ glayout->addMultiCellWidget(vHandleLeft, 0, 2, 0, 0);
+ glayout->addMultiCellWidget(vHandleRight, 0, 2, 2, 2);
+ glayout->addLayout(hbox, 1, 1);
+ glayout->setRowStretch(1, 1);
+ glayout->setColStretch(1, 1);
+
+ hHandleBottom->hide();
+ vHandleRight->hide();
+ hHandleTop->hide();
+ vHandleLeft->hide();
+ setFrameStyle(Q3Frame::StyledPanel | Q3Frame::Raised);
+ setLineWidth(2);
+
+ if (parentWidget())
+ parentWidget()->installEventFilter(this);
+ QWidget *mw = parentWidget();
+ Q3DockArea *da = qobject_cast<Q3DockArea*>(parentWidget());
+ if (da) {
+ if (curPlace == InDock)
+ da->moveDockWindow(this);
+ mw = da->parentWidget();
+ }
+ if (qobject_cast<Q3MainWindow*>(mw)) {
+ if (place() == InDock) {
+ Qt::Dock myDock = Qt::DockTop;
+ // make sure we put the window in the correct dock.
+ if (dockArea) {
+ Q3MainWindow *mainw = (Q3MainWindow*)mw;
+ // I'm not checking if it matches the top because I've
+ // done the assignment to it above.
+ if (dockArea == mainw->leftDock())
+ myDock = Qt::DockLeft;
+ else if (dockArea == mainw->rightDock())
+ myDock = Qt::DockRight;
+ else if (dockArea == mainw->bottomDock())
+ myDock = Qt::DockBottom;
+ }
+ ((Q3MainWindow*)mw)->addDockWindow(this, myDock);
+ }
+ moveEnabled = ((Q3MainWindow*)mw)->dockWindowsMovable();
+ opaque = ((Q3MainWindow*)mw)->opaqueMoving();
+ }
+
+ updateGui();
+
+ connect(titleBar, SIGNAL(doubleClicked()), this, SLOT(dock()));
+ connect(verHandle, SIGNAL(doubleClicked()), this, SLOT(undock()));
+ connect(horHandle, SIGNAL(doubleClicked()), this, SLOT(undock()));
+ connect(this, SIGNAL(orientationChanged(Qt::Orientation)),
+ this, SLOT(setOrientation(Qt::Orientation)));
+}
+
+/*!
+ Sets the orientation of the dock window to \a o. The orientation
+ is propagated to the layout boxLayout().
+
+ \warning All undocked Q3ToolBars will always have a horizontal orientation.
+*/
+
+void Q3DockWindow::setOrientation(Qt::Orientation o)
+{
+ QGridLayout *glayout = (QGridLayout*)layout();
+ glayout->removeWidget(hHandleTop);
+ glayout->removeWidget(hHandleBottom);
+ glayout->removeWidget(vHandleLeft);
+ glayout->removeWidget(vHandleRight);
+
+ if (o == Qt::Horizontal) {
+ // Set up the new layout as
+ // 3 3 3 1 = vHandleLeft 4 = hHandleBottom
+ // 1 X 2 2 = vHandleRight X = Inner Layout
+ // 4 4 4 3 = hHandleTop
+ glayout->addMultiCellWidget(hHandleTop, 0, 0, 0, 2);
+ glayout->addMultiCellWidget(hHandleBottom, 2, 2, 0, 2);
+ glayout->addMultiCellWidget(vHandleLeft, 1, 1, 0, 0);
+ glayout->addMultiCellWidget(vHandleRight, 1, 1, 2, 2);
+ } else {
+ // Set up the new layout as
+ // 1 3 2 1 = vHandleLeft 4 = hHandleBottom
+ // 1 X 2 2 = vHandleRight X = Inner Layout
+ // 1 4 2 3 = hHandleTop
+ glayout->addMultiCellWidget(hHandleTop, 0, 0, 1, 1);
+ glayout->addMultiCellWidget(hHandleBottom, 2, 2, 1, 1);
+ glayout->addMultiCellWidget(vHandleLeft, 0, 2, 0, 0);
+ glayout->addMultiCellWidget(vHandleRight, 0, 2, 2, 2);
+ }
+ boxLayout()->setDirection(o == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom);
+ QApplication::sendPostedEvents(this, QEvent::LayoutHint);
+ QEvent *e = new QEvent(QEvent::LayoutHint);
+ QApplication::postEvent(this, e);
+}
+
+/*!
+ Destroys the dock window and its child widgets.
+*/
+
+Q3DockWindow::~Q3DockWindow()
+{
+ qApp->removeEventFilter(this);
+ if (area())
+ area()->removeDockWindow(this, false, false);
+ Q3DockArea *a = area();
+ if (!a && dockWindowData)
+ a = ((Q3DockArea::DockWindowData*)dockWindowData)->area;
+ Q3MainWindow *mw = a ? qobject_cast<Q3MainWindow*>(a->parentWidget()) : 0;
+ if (mw)
+ mw->removeDockWindow(this);
+
+ delete (Q3DockArea::DockWindowData*)dockWindowData;
+}
+
+/*! \reimp
+*/
+
+void Q3DockWindow::resizeEvent(QResizeEvent *e)
+{
+ Q3Frame::resizeEvent(e);
+ updateGui();
+}
+
+
+void Q3DockWindow::swapRect(QRect &r, Qt::Orientation o, const QPoint &offset, Q3DockArea *)
+{
+ r.setSize(QSize(r.height(), r.width()));
+ bool reverse = QApplication::reverseLayout();
+ if (o == Qt::Horizontal)
+ r.moveBy(-r.width()/2, 0);
+ else
+ r.moveBy(reverse ? - r.width() : 0, -r.height() / 2 );
+ r.moveBy(offset.x(), offset.y());
+}
+
+QWidget *Q3DockWindow::areaAt(const QPoint &gp)
+{
+ QWidget *w = qApp->widgetAt(gp);
+
+ if (w && (w == this || w == titleBar) && parentWidget())
+ w = parentWidget()->childAt(parentWidget()->mapFromGlobal(gp));
+
+ while (w) {
+ if (qobject_cast<Q3DockArea*>(w)) {
+ Q3DockArea *a = (Q3DockArea*)w;
+ if (a->isDockWindowAccepted(this))
+ return w;
+ }
+ if (qobject_cast<Q3MainWindow*>(w)) {
+ Q3MainWindow *mw = (Q3MainWindow*)w;
+ Q3DockArea *a = mw->dockingArea(mw->mapFromGlobal(gp));
+ if (a && a->isDockWindowAccepted(this))
+ return a;
+ }
+ w = w->isWindow() ? 0 : (QWidget *)w->parent();
+ }
+ return 0;
+}
+
+void Q3DockWindow::handleMove(const QPoint &pos, const QPoint &gp, bool drawRect)
+{
+ if (!rubberBand)
+ return;
+
+ currRect = QRect(realWidgetPos(this), size());
+ QWidget *w = areaAt(gp);
+ if (titleBar->ctrlDown || horHandle->ctrlDown || verHandle->ctrlDown)
+ w = 0;
+ currRect.moveBy(pos.x(), pos.y());
+ if (!qobject_cast<Q3DockArea*>(w)) {
+ if (startOrientation != Qt::Horizontal && qobject_cast<Q3ToolBar*>(this))
+ swapRect(currRect, Qt::Horizontal, startOffset, (Q3DockArea*)w);
+ if (drawRect) {
+ rubberBand->setGeometry(currRect);
+ } else {
+ QPoint mp(mapToGlobal(pos));
+ if(place() == InDock) {
+ undock();
+ if(titleBar) {
+ mp = QPoint(titleBar->width() / 2, titleBar->height() / 2);
+ QMouseEvent me(QEvent::MouseButtonPress, mp, Qt::LeftButton, 0);
+ QApplication::sendEvent(titleBar, &me);
+ mp = titleBar->mapToGlobal(mp);
+ }
+ }
+ move(mp);
+ }
+ state = OutsideDock;
+ return;
+ }
+
+ Q3DockArea *area = (Q3DockArea*)w;
+ if(area->isVisible()) {
+ state = InDock;
+ Qt::Orientation o = (area ? area->orientation() :
+ (boxLayout()->direction() == QBoxLayout::LeftToRight ||
+ boxLayout()->direction() == QBoxLayout::RightToLeft ?
+ Qt::Horizontal : Qt::Vertical));
+ if (startOrientation != o)
+ swapRect(currRect, o, startOffset, area);
+ if (drawRect) {
+ rubberBand->setGeometry(currRect);
+ }
+ tmpDockArea = area;
+ }
+}
+
+void Q3DockWindow::updateGui()
+{
+ if (curPlace == OutsideDock) {
+ hbox->setMargin(2);
+ horHandle->hide();
+ verHandle->hide();
+ if (moveEnabled)
+ titleBar->show();
+ else
+ titleBar->hide();
+ titleBar->updateGui();
+ hHandleTop->hide();
+ vHandleLeft->hide();
+ hHandleBottom->hide();
+ vHandleRight->hide();
+ setLineWidth(2);
+ widgetResizeHandler->setActive(isResizeEnabled());
+ } else {
+ hbox->setMargin(0);
+ titleBar->hide();
+ if (orientation() == Qt::Horizontal) {
+ horHandle->hide();
+ if (moveEnabled)
+ verHandle->show();
+ else
+ verHandle->hide();
+#ifdef Q_WS_MAC
+ if(horHandle->mousePressed) {
+ horHandle->mousePressed = false;
+ verHandle->mousePressed = true;
+ verHandle->grabMouse();
+ }
+#endif
+ verHandle->updateGui();
+ } else {
+ if (moveEnabled)
+ horHandle->show();
+ else
+ horHandle->hide();
+ horHandle->updateGui();
+#ifdef Q_WS_MAC
+ if(verHandle->mousePressed) {
+ verHandle->mousePressed = false;
+ horHandle->mousePressed = true;
+ horHandle->grabMouse();
+ }
+#endif
+ verHandle->hide();
+ }
+ if (isResizeEnabled()) {
+ if (orientation() == Qt::Horizontal) {
+ hHandleBottom->raise();
+ hHandleTop->raise();
+ } else {
+ vHandleRight->raise();
+ vHandleLeft->raise();
+ }
+
+ if (area()) {
+ if (orientation() == Qt::Horizontal) {
+ if (area()->handlePosition() == Q3DockArea::Normal) {
+ hHandleBottom->show();
+ hHandleTop->hide();
+ } else {
+ hHandleTop->show();
+ hHandleBottom->hide();
+ }
+ if (!area()->isLastDockWindow(this))
+ vHandleRight->show();
+ else
+ vHandleRight->hide();
+ vHandleLeft->hide();
+ } else {
+ if ((area()->handlePosition() == Q3DockArea::Normal) != QApplication::reverseLayout()) {
+ vHandleRight->show();
+ vHandleLeft->hide();
+ } else {
+ vHandleLeft->show();
+ vHandleRight->hide();
+ }
+ if (!area()->isLastDockWindow(this))
+ hHandleBottom->show();
+ else
+ hHandleBottom->hide();
+ hHandleTop->hide();
+ }
+ }
+ }
+#ifndef Q_OS_WINCE
+ if (moveEnabled)
+ setLineWidth(1);
+ else
+ setLineWidth(0);
+#endif
+ widgetResizeHandler->setActive(false);
+ }
+}
+
+void Q3DockWindow::updatePosition(const QPoint &globalPos)
+{
+ if (curPlace == OutsideDock && state == InDock)
+ lastSize = size();
+
+ bool doAdjustSize = curPlace != state && state == OutsideDock;
+ bool doUpdate = true;
+ bool doOrientationChange = true;
+ if (state != curPlace && state == InDock) {
+ doUpdate = false;
+ curPlace = state;
+ updateGui();
+ QApplication::sendPostedEvents();
+ }
+ Qt::Orientation oo = orientation();
+
+ if (state == InDock) {
+ if (tmpDockArea) {
+ bool differentDocks = false;
+ if (dockArea && dockArea != tmpDockArea) {
+ differentDocks = true;
+ delete (Q3DockArea::DockWindowData*)dockWindowData;
+ dockWindowData = dockArea->dockWindowData(this);
+ dockArea->removeDockWindow(this, false, false);
+ }
+ dockArea = tmpDockArea;
+ if (differentDocks) {
+ if (doUpdate) {
+ doUpdate = false;
+ curPlace = state;
+ updateGui();
+ }
+ emit orientationChanged(tmpDockArea->orientation());
+ doOrientationChange = false;
+ } else {
+ updateGui();
+ }
+ dockArea->moveDockWindow(this, globalPos, currRect, startOrientation != oo);
+ }
+ } else {
+ if (dockArea) {
+ Q3MainWindow *mw = (Q3MainWindow*)dockArea->parentWidget();
+ if (qobject_cast<Q3MainWindow*>(mw) &&
+ (!mw->isDockEnabled(Qt::DockTornOff) ||
+ !mw->isDockEnabled(this, Qt::DockTornOff)))
+ return;
+ delete (Q3DockArea::DockWindowData*)dockWindowData;
+ dockWindowData = dockArea->dockWindowData(this);
+ dockArea->removeDockWindow(this, true,
+ startOrientation != Qt::Horizontal && qobject_cast<Q3ToolBar*>(this));
+ }
+ dockArea = 0;
+ QPoint topLeft = currRect.topLeft();
+ QRect screen = qApp->desktop()->availableGeometry(topLeft);
+ if (!screen.contains(topLeft)) {
+ topLeft.setY(qMax(topLeft.y(), screen.top()));
+ topLeft.setY(qMin(topLeft.y(), screen.bottom()-height()));
+ topLeft.setX(qMax(topLeft.x(), screen.left()));
+ topLeft.setX(qMin(topLeft.x(), screen.right()-width()));
+ }
+ move(topLeft);
+ }
+
+ if (curPlace == InDock && state == OutsideDock && !qobject_cast<Q3ToolBar*>(this)) {
+ if (lastSize != QSize(-1, -1))
+ resize(lastSize);
+ }
+
+ if (doUpdate) {
+ curPlace = state;
+ updateGui();
+ }
+ if (doOrientationChange)
+ emit orientationChanged(orientation());
+ tmpDockArea = 0;
+ if (doAdjustSize) {
+ QApplication::sendPostedEvents(this, QEvent::LayoutHint);
+ if (qobject_cast<Q3ToolBar*>(this))
+ adjustSize();
+ if (lastSize == QSize(-1, -1))
+ setAttribute(Qt::WA_Resized, false); // Ensures size is recalculated (non-opaque).
+ show();
+ if (parentWidget() && isWindow())
+ parentWidget()->setActiveWindow();
+
+ }
+
+ emit placeChanged(curPlace);
+}
+
+/*!
+ Sets the dock window's main widget to \a w.
+
+ \sa boxLayout()
+*/
+
+void Q3DockWindow::setWidget(QWidget *w)
+{
+ wid = w;
+ boxLayout()->addWidget(w);
+ updateGui();
+}
+
+/*!
+ Returns the dock window's main widget.
+
+ \sa setWidget()
+*/
+
+QWidget *Q3DockWindow::widget() const
+{
+ return wid;
+}
+
+void Q3DockWindow::startRectDraw(const QPoint &so, bool drawRect)
+{
+ state = place();
+ if (rubberBand)
+ endRectDraw(!opaque);
+ rubberBand = new QRubberBand(QRubberBand::Rectangle);
+ currRect = QRect(realWidgetPos(this), size());
+ if (drawRect) {
+ rubberBand->setGeometry(currRect);
+ }
+ startOrientation = orientation();
+ startOffset = mapFromGlobal(so);
+ rubberBand->show();
+}
+
+void Q3DockWindow::endRectDraw(bool)
+{
+ delete rubberBand;
+ rubberBand = 0;
+}
+
+/*!
+ \reimp
+*/
+void Q3DockWindow::drawFrame(QPainter *p)
+{
+ if (place() == InDock) {
+ Q3Frame::drawFrame(p);
+ return;
+ }
+
+ QStyleOptionFrame opt;
+ opt.rect = rect();
+ opt.palette = palette();
+ opt.state = QStyle::State_None;
+ if (titleBar->isActive())
+ opt.state |= QStyle::State_Active;
+ opt.lineWidth = lineWidth();
+ opt.midLineWidth = midLineWidth();
+
+ style()->drawPrimitive(QStyle::PE_FrameWindow, &opt, p, this);
+}
+
+/*!
+ \reimp
+*/
+void Q3DockWindow::drawContents(QPainter *p)
+{
+ // This is only used by the PocketPC style. We probably need to revist later.
+ QStyleOption opt(0, QStyleOption::SO_Default);
+ opt.init(this);
+ if (titleBar->isActive())
+ opt.state |= QStyle::State_Active;
+ style()->drawControl(QStyle::CE_Q3DockWindowEmptyArea, &opt, p, this);
+}
+
+/*!
+ \property Q3DockWindow::resizeEnabled
+ \brief whether the dock window is resizeable
+
+ A resizeable dock window can be resized using splitter-like
+ handles inside a dock area and like every other top level window
+ when floating.
+
+ A dock window is both horizontally and vertically stretchable if
+ you call setResizeEnabled(true).
+
+ This property is false by default.
+
+ \sa setVerticallyStretchable() setHorizontallyStretchable()
+*/
+
+void Q3DockWindow::setResizeEnabled(bool b)
+{
+ resizeEnabled = b;
+ updateGui();
+}
+
+/*!
+ \property Q3DockWindow::movingEnabled
+ \brief whether the user can move the dock window within the dock
+ area, move the dock window to another dock area, or float the dock
+ window.
+
+ This property is true by default.
+*/
+
+void Q3DockWindow::setMovingEnabled(bool b)
+{
+ moveEnabled = b;
+ updateGui();
+}
+
+bool Q3DockWindow::isResizeEnabled() const
+{
+ return resizeEnabled;
+}
+
+bool Q3DockWindow::isMovingEnabled() const
+{
+ return moveEnabled;
+}
+
+/*!
+ \property Q3DockWindow::closeMode
+ \brief the close mode of a dock window
+
+ Defines when (if ever) the dock window has a close button. The
+ choices are \c Never, \c Docked (i.e. only when docked), \c
+ Undocked (only when undocked, i.e. floated) or \c Always.
+
+ The default is \c Never.
+*/
+
+void Q3DockWindow::setCloseMode(int m)
+{
+ cMode = m;
+ if (place() == InDock) {
+ horHandle->updateGui();
+ verHandle->updateGui();
+ } else {
+ titleBar->updateGui();
+ }
+}
+
+/*!
+ Returns true if the dock window has a close button; otherwise
+ returns false. The result depends on the dock window's \l Place
+ and its \l CloseMode.
+
+ \sa setCloseMode()
+*/
+
+bool Q3DockWindow::isCloseEnabled() const
+{
+ return (((cMode & Docked) == Docked && place() == InDock) ||
+ ((cMode & Undocked) == Undocked && place() == OutsideDock));
+}
+
+int Q3DockWindow::closeMode() const
+{
+ return cMode;
+}
+
+/*!
+ \property Q3DockWindow::horizontallyStretchable
+ \brief whether the dock window is horizontally stretchable.
+
+ A dock window is horizontally stretchable if you call
+ setHorizontallyStretchable(true) or setResizeEnabled(true).
+
+ \warning Stretchability is broken. You must call
+ setResizeEnabled(true) to get proper behavior and even then
+ Q3DockWindow does not limit stretchablilty.
+
+ \sa setResizeEnabled()
+*/
+
+void Q3DockWindow::setHorizontallyStretchable(bool b)
+{
+ stretchable[Qt::Horizontal] = b;
+}
+
+/*!
+ \property Q3DockWindow::verticallyStretchable
+ \brief whether the dock window is vertically stretchable.
+
+ A dock window is vertically stretchable if you call
+ setVerticallyStretchable(true) or setResizeEnabled(true).
+
+ \sa setResizeEnabled()
+
+ \warning Stretchability is broken. You must call
+ setResizeEnabled(true) to get proper behavior and even then
+ Q3DockWindow does not limit stretchablilty.
+*/
+
+void Q3DockWindow::setVerticallyStretchable(bool b)
+{
+ stretchable[Qt::Vertical] = b;
+}
+
+bool Q3DockWindow::isHorizontallyStretchable() const
+{
+ return isResizeEnabled() || stretchable[Qt::Horizontal];
+}
+
+bool Q3DockWindow::isVerticallyStretchable() const
+{
+ return isResizeEnabled() || stretchable[Qt::Vertical];
+}
+
+/*!
+ \property Q3DockWindow::stretchable
+ \brief whether the dock window is stretchable in the current
+ orientation()
+
+ This property can be set using setHorizontallyStretchable() and
+ setVerticallyStretchable(), or with setResizeEnabled().
+
+ \warning Stretchability is broken. You must call
+ setResizeEnabled(true) to get proper behavior and even then
+ Q3DockWindow does not limit stretchablilty.
+
+ \sa setResizeEnabled()
+*/
+
+bool Q3DockWindow::isStretchable() const
+{
+ if (orientation() == Qt::Horizontal)
+ return isHorizontallyStretchable();
+ return isVerticallyStretchable();
+}
+
+/*!
+ Returns the orientation of the dock window.
+
+ \sa orientationChanged()
+*/
+
+Qt::Orientation Q3DockWindow::orientation() const
+{
+ if (dockArea)
+ return dockArea->orientation();
+ if (qobject_cast<const Q3ToolBar*>(this))
+ return Qt::Horizontal;
+ return (((Q3DockWindow*)this)->boxLayout()->direction() == QBoxLayout::LeftToRight ||
+ ((Q3DockWindow*)this)->boxLayout()->direction() == QBoxLayout::RightToLeft ?
+ Qt::Horizontal : Qt::Vertical);
+}
+
+int Q3DockWindow::offset() const
+{
+ return offs;
+}
+
+/*!
+ \property Q3DockWindow::offset
+ \brief the dock window's preferred offset from the dock area's
+ left edge (top edge for vertical dock areas)
+
+ The default is 0.
+*/
+
+void Q3DockWindow::setOffset(int o)
+{
+ offs = o;
+}
+
+/*!
+ Returns the dock window's preferred size (fixed extent).
+
+ \sa setFixedExtentWidth() setFixedExtentHeight()
+*/
+
+QSize Q3DockWindow::fixedExtent() const
+{
+ return fExtent;
+}
+
+/*!
+ Sets the dock window's preferred width for its fixed extent (size)
+ to \a w.
+
+ \sa setFixedExtentHeight()
+*/
+
+void Q3DockWindow::setFixedExtentWidth(int w)
+{
+ fExtent.setWidth(w);
+}
+
+/*!
+ Sets the dock window's preferred height for its fixed extent
+ (size) to \a h.
+
+ \sa setFixedExtentWidth()
+*/
+
+void Q3DockWindow::setFixedExtentHeight(int h)
+{
+ fExtent.setHeight(h);
+}
+
+/*!
+ \property Q3DockWindow::newLine
+ \brief whether the dock window prefers to start a new line in the
+ dock area.
+
+ The default is false, i.e. the dock window doesn't require a new
+ line in the dock area.
+*/
+
+void Q3DockWindow::setNewLine(bool b)
+{
+ nl = b;
+}
+
+bool Q3DockWindow::newLine() const
+{
+ return nl;
+}
+
+/*!
+ Returns the layout which is used for adding widgets to the dock
+ window. The layout's orientation is set automatically to match the
+ orientation of the dock window. You can add widgets to the layout
+ using the box layout's QBoxLayout::addWidget() function.
+
+ If the dock window only needs to contain a single widget use
+ setWidget() instead.
+
+ \sa setWidget() setOrientation()
+*/
+
+QBoxLayout *Q3DockWindow::boxLayout()
+{
+ return childBox;
+}
+
+/*! \reimp
+ */
+
+QSize Q3DockWindow::sizeHint() const
+{
+ QSize sh(Q3Frame::sizeHint());
+ if (place() == InDock)
+ sh = sh.expandedTo(fixedExtent());
+ sh = sh.expandedTo(QSize(16, 16));
+ if (area()) {
+ if (area()->orientation() == Qt::Horizontal && !vHandleRight->isVisible())
+ sh.setWidth(sh.width() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3);
+ else if (area()->orientation() == Qt::Vertical && !hHandleBottom->isVisible())
+ sh.setHeight(sh.height() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3);
+ }
+ return sh;
+}
+
+/*! \internal
+ */
+
+QSize Q3DockWindow::minimumSize() const
+{
+ QSize ms(Q3Frame::minimumSize());
+ if (place() == InDock)
+ ms = ms.expandedTo(fixedExtent());
+ ms = ms.expandedTo(QSize(16, 16));
+ if (area()) {
+ if (area()->orientation() == Qt::Horizontal && !vHandleRight->isVisible())
+ ms.setWidth(ms.width() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3);
+ else if (area()->orientation() == Qt::Vertical && !hHandleBottom->isVisible())
+ ms.setHeight(ms.height() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3);
+ }
+ return ms;
+}
+
+/*! \reimp
+ */
+
+QSize Q3DockWindow::minimumSizeHint() const
+{
+ QSize msh(Q3Frame::minimumSize());
+ if (place() == InDock)
+ msh = msh.expandedTo(fixedExtent());
+ msh = msh.expandedTo(QSize(16, 16));
+ if (area()) {
+ if (area()->orientation() == Qt::Horizontal && !vHandleRight->isVisible())
+ msh.setWidth(msh.width() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3);
+ else if (area()->orientation() == Qt::Vertical && !hHandleBottom->isVisible())
+ msh.setHeight(msh.height() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3);
+ }
+ return msh;
+}
+
+/*!
+ \fn void Q3DockWindow::undock()
+
+ Undocks the Q3DockWindow from its current dock area if it is
+ docked; otherwise does nothing.
+
+ \sa dock() Q3DockArea::moveDockWindow(),
+ Q3DockArea::removeDockWindow(), Q3MainWindow::moveDockWindow(),
+ Q3MainWindow::removeDockWindow()
+*/
+
+/*!
+ \fn void Q3DockWindow::undock(QWidget *widget)
+
+ Undocks the specified \a widget from its current dock area if it is
+ docked; otherwise does nothing.
+
+ \sa dock() Q3DockArea::moveDockWindow(),
+ Q3DockArea::removeDockWindow(), Q3MainWindow::moveDockWindow(),
+ Q3MainWindow::removeDockWindow()
+*/
+void Q3DockWindow::undock(QWidget *w)
+{
+ Q3MainWindow *mw = 0;
+ if (area())
+ mw = qobject_cast<Q3MainWindow*>(area()->parentWidget());
+ if (mw && !mw->isDockEnabled(this, Qt::DockTornOff))
+ return;
+ if ((place() == OutsideDock && !w))
+ return;
+
+ QPoint p(50, 50);
+ if (window())
+ p = window()->pos() + QPoint(20, 20);
+ if (dockArea) {
+ delete (Q3DockArea::DockWindowData*)dockWindowData;
+ dockWindowData = dockArea->dockWindowData(this);
+ dockArea->removeDockWindow(this, true, orientation() != Qt::Horizontal && qobject_cast<Q3ToolBar*>(this));
+ }
+ dockArea = 0;
+ if (lastPos != QPoint(-1, -1) && lastPos.x() > 0 && lastPos.y() > 0)
+ move(lastPos);
+ else
+ move(p);
+ if (lastSize != QSize(-1, -1))
+ resize(lastSize);
+ curPlace = OutsideDock;
+ updateGui();
+ emit orientationChanged(orientation());
+ QApplication::sendPostedEvents(this, QEvent::LayoutHint);
+ if (qobject_cast<Q3ToolBar*>(this))
+ adjustSize();
+ if (!w) {
+ if (!parentWidget() || parentWidget()->isVisible()) {
+ if (lastSize == QSize(-1, -1))
+ setAttribute(Qt::WA_Resized, false);// Ensures size is recalculated (opaque).
+ show();
+ }
+ } else {
+ setParent(w, 0);
+ move(-width() - 5, -height() - 5);
+ resize(1, 1);
+ show();
+ }
+ if (parentWidget() && isWindow())
+ parentWidget()->setActiveWindow();
+ emit placeChanged(place());
+}
+
+void Q3DockWindow::removeFromDock(bool fixNewLines)
+{
+ if (dockArea)
+ dockArea->removeDockWindow(this, false, false, fixNewLines);
+}
+
+/*!
+ Docks the dock window into the last dock area in which it was
+ docked.
+
+ If the dock window has no last dock area (e.g. it was created as a
+ floating window and has never been docked), or if the last dock
+ area it was docked in does not exist (e.g. the dock area has been
+ deleted), nothing happens.
+
+ The dock window will dock with the dock area regardless of the return value
+ of Q3DockArea::isDockWindowAccepted().
+
+ \sa undock() Q3DockArea::moveDockWindow(),
+ Q3DockArea::removeDockWindow(), Q3MainWindow::moveDockWindow(),
+ Q3MainWindow::removeDockWindow(), Q3DockArea::isDockWindowAccepted()
+
+*/
+
+void Q3DockWindow::dock()
+{
+ if (!(Q3DockArea::DockWindowData*)dockWindowData ||
+ !((Q3DockArea::DockWindowData*)dockWindowData)->area)
+ return;
+ curPlace = InDock;
+ lastPos = pos();
+ lastSize = size();
+ ((Q3DockArea::DockWindowData*)dockWindowData)->
+ area->dockWindow(this, (Q3DockArea::DockWindowData*)dockWindowData);
+ emit orientationChanged(orientation());
+ emit placeChanged(place());
+}
+
+/*! \reimp
+ */
+
+void Q3DockWindow::hideEvent(QHideEvent *e)
+{
+ Q3Frame::hideEvent(e);
+}
+
+/*! \reimp
+ */
+
+void Q3DockWindow::showEvent(QShowEvent *e)
+{
+ if (curPlace == OutsideDock && (parent() && parent()->objectName() == QLatin1String("qt_hide_dock"))) {
+ QRect sr = qApp->desktop()->availableGeometry(this);
+ if (!sr.contains(pos())) {
+ int nx = qMin(qMax(x(), sr.x()), sr.right()-width());
+ int ny = qMin(qMax(y(), sr.y()), sr.bottom()-height());
+ move(nx, ny);
+ }
+ }
+
+ Q3Frame::showEvent(e);
+}
+
+/*!
+ \property Q3DockWindow::opaqueMoving
+ \brief whether the dock window will be shown normally whilst it is
+ being moved.
+
+ If this property is false, (the default), the dock window will be
+ represented by an outline rectangle whilst it is being moved.
+
+ \warning Currently opaque moving has some problems and we do not
+ recommend using it at this time. We expect to fix these problems
+ in a future release.
+*/
+
+void Q3DockWindow::setOpaqueMoving(bool b)
+{
+ opaque = b;
+ horHandle->setOpaqueMoving(b);
+ verHandle->setOpaqueMoving(b);
+ titleBar->setOpaqueMoving(b);
+}
+
+bool Q3DockWindow::opaqueMoving() const
+{
+ return opaque;
+}
+
+void Q3DockWindow::updateSplitterVisibility(bool visible)
+{
+ if (area() && isResizeEnabled()) {
+ if (orientation() == Qt::Horizontal) {
+ if (visible)
+ vHandleRight->show();
+ else
+ vHandleRight->hide();
+ vHandleLeft->hide();
+ } else {
+ if (visible)
+ hHandleBottom->show();
+ else
+ hHandleBottom->hide();
+ hHandleTop->hide();
+ }
+ }
+}
+
+/*! \reimp */
+bool Q3DockWindow::eventFilter(QObject * o, QEvent *e)
+{
+ if (!o->isWidgetType())
+ return false;
+
+ if (e->type() == QEvent::KeyPress &&
+ (horHandle->mousePressed ||
+ verHandle->mousePressed ||
+ titleBar->mousePressed)) {
+ QKeyEvent *ke = (QKeyEvent*)e;
+ if (ke->key() == Qt::Key_Escape) {
+ horHandle->mousePressed =
+ verHandle->mousePressed =
+ titleBar->mousePressed = false;
+ endRectDraw(!opaque);
+ qApp->removeEventFilter(this);
+ return true;
+ }
+ } else if (((QWidget*)o)->window() != this && place() == OutsideDock && isWindow()) {
+ if ((e->type() == QEvent::WindowDeactivate ||
+ e->type() == QEvent::WindowActivate))
+ event(e);
+ }
+ return false;
+}
+
+/*! \reimp */
+bool Q3DockWindow::event(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::WindowDeactivate:
+ if (place() == OutsideDock && isWindow() && parentWidget()
+ && parentWidget()->isActiveWindow())
+ return true;
+ break;
+ case QEvent::HideToParent:
+ emit visibilityChanged(false);
+ break;
+ case QEvent::ShowToParent:
+ emit visibilityChanged(true);
+ break;
+ case QEvent::WindowTitleChange:
+ {
+ QString s = Q3Frame::windowTitle();
+ titleBar->setWindowTitle(s);
+#ifndef QT_NO_TOOLTIP
+ horHandle->setToolTip(s);
+ verHandle->setToolTip(s);
+#endif
+ break;
+ }
+ default:
+ break;
+ }
+ return Q3Frame::event(e);
+}
+
+/*!
+ Returns the dock window's title.
+*/
+QString Q3DockWindow::windowTitle() const
+{
+ return titleBar->windowTitle();
+}
+
+/*! \reimp */
+void Q3DockWindow::contextMenuEvent(QContextMenuEvent *e)
+{
+ QObject *o = this;
+ while (o) {
+ if (qobject_cast<Q3MainWindow*>(o))
+ break;
+ o = o->parent();
+ }
+ if (!o || ! ((Q3MainWindow*)o)->showDockMenu(e->globalPos()))
+ e->ignore();
+}
+
+QT_END_NAMESPACE
+
+#include "q3dockwindow.moc"
+
+#endif //QT_NO_MAINWINDOW
diff --git a/src/qt3support/widgets/q3dockwindow.h b/src/qt3support/widgets/q3dockwindow.h
new file mode 100644
index 0000000..bf53a82
--- /dev/null
+++ b/src/qt3support/widgets/q3dockwindow.h
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DOCKWINDOW_H
+#define Q3DOCKWINDOW_H
+
+#include <Qt3Support/q3frame.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_MAINWINDOW
+
+class Q3DockWindowHandle;
+class Q3DockWindowTitleBar;
+class QPainter;
+class Q3DockWindowResizeHandle;
+class QBoxLayout;
+class QHBoxLayout;
+class QVBoxLayout;
+class Q3DockArea;
+class QWidgetResizeHandler;
+class Q3MainWindow;
+class Q3DockAreaLayout;
+class Q3DockWindowPrivate;
+class Q3ToolBar;
+class QWindowsXPStyle;
+class QRubberBand;
+
+class Q_COMPAT_EXPORT Q3DockWindow : public Q3Frame
+{
+ Q_OBJECT
+ Q_ENUMS(CloseMode Place)
+ Q_PROPERTY(int closeMode READ closeMode WRITE setCloseMode)
+ Q_PROPERTY(bool resizeEnabled READ isResizeEnabled WRITE setResizeEnabled)
+ Q_PROPERTY(bool movingEnabled READ isMovingEnabled WRITE setMovingEnabled)
+ Q_PROPERTY(bool horizontallyStretchable READ isHorizontallyStretchable WRITE setHorizontallyStretchable)
+ Q_PROPERTY(bool verticallyStretchable READ isVerticallyStretchable WRITE setVerticallyStretchable)
+ Q_PROPERTY(bool stretchable READ isStretchable)
+ Q_PROPERTY(bool newLine READ newLine WRITE setNewLine)
+ Q_PROPERTY(bool opaqueMoving READ opaqueMoving WRITE setOpaqueMoving)
+ Q_PROPERTY(int offset READ offset WRITE setOffset)
+ Q_PROPERTY(Place place READ place)
+
+ friend class Q3DockWindowHandle;
+ friend class Q3DockWindowTitleBar;
+ friend class Q3DockArea;
+ friend class Q3DockAreaLayout;
+ friend class Q3MainWindow;
+ friend class QCEMainWindow;
+ friend class Q3ToolBar;
+ friend class QWindowsXPStyle;
+
+public:
+ enum Place { InDock, OutsideDock };
+ enum CloseMode { Never = 0, Docked = 1, Undocked = 2, Always = Docked | Undocked };
+
+ Q3DockWindow(Place p = InDock, QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0);
+ Q3DockWindow(QWidget* parent, const char* name=0, Qt::WindowFlags f = 0);
+ ~Q3DockWindow();
+
+ virtual void setWidget(QWidget *w);
+ QWidget *widget() const;
+
+ Place place() const { return curPlace; }
+
+ Q3DockArea *area() const;
+
+ virtual void setCloseMode(int m);
+ bool isCloseEnabled() const;
+ int closeMode() const;
+
+ virtual void setResizeEnabled(bool b);
+ virtual void setMovingEnabled(bool b);
+ bool isResizeEnabled() const;
+ bool isMovingEnabled() const;
+
+ virtual void setHorizontallyStretchable(bool b);
+ virtual void setVerticallyStretchable(bool b);
+ bool isHorizontallyStretchable() const;
+ bool isVerticallyStretchable() const;
+ void setHorizontalStretchable(bool b) { setHorizontallyStretchable(b); }
+ void setVerticalStretchable(bool b) { setVerticallyStretchable(b); }
+ bool isHorizontalStretchable() const { return isHorizontallyStretchable(); }
+ bool isVerticalStretchable() const { return isVerticallyStretchable(); }
+ bool isStretchable() const;
+
+ virtual void setOffset(int o);
+ int offset() const;
+
+ virtual void setFixedExtentWidth(int w);
+ virtual void setFixedExtentHeight(int h);
+ QSize fixedExtent() const;
+
+ virtual void setNewLine(bool b);
+ bool newLine() const;
+
+ Qt::Orientation orientation() const;
+
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QSize minimumSizeHint() const;
+
+ QBoxLayout *boxLayout();
+
+ virtual void setOpaqueMoving(bool b);
+ bool opaqueMoving() const;
+
+ bool eventFilter(QObject *o, QEvent *e);
+
+ QString windowTitle() const;
+
+Q_SIGNALS:
+ void orientationChanged(Qt::Orientation o);
+ void placeChanged(Q3DockWindow::Place p);
+ void visibilityChanged(bool);
+
+public Q_SLOTS:
+ virtual void undock(QWidget *w);
+ virtual void undock() { undock(0); }
+ virtual void dock();
+ virtual void setOrientation(Qt::Orientation o);
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+ void showEvent(QShowEvent *e);
+ void hideEvent(QHideEvent *e);
+ void contextMenuEvent(QContextMenuEvent *e);
+
+ void drawFrame(QPainter *);
+ void drawContents(QPainter *);
+
+ bool event(QEvent *e);
+
+private Q_SLOTS:
+ void toggleVisible() { setVisible(!isVisible()); }
+
+private:
+ Q3DockWindow(Place p, QWidget* parent, const char* name, Qt::WindowFlags f, bool toolbar);
+
+ void handleMove(const QPoint &pos, const QPoint &gp, bool drawRect);
+ void updateGui();
+ void updateSplitterVisibility(bool visible);
+
+ void startRectDraw(const QPoint &so, bool drawRect);
+ void endRectDraw(bool drawRect);
+ void updatePosition(const QPoint &globalPos );
+ QWidget *areaAt(const QPoint &gp);
+ void removeFromDock(bool fixNewLines = true);
+ void swapRect(QRect &r, Qt::Orientation o, const QPoint &offset, Q3DockArea *area);
+ void init();
+
+private:
+ Q3DockWindowHandle *horHandle, *verHandle;
+ Q3DockWindowTitleBar *titleBar;
+ QWidget *wid;
+ QRubberBand *rubberBand;
+ Q3DockArea *dockArea, *tmpDockArea;
+ QRect currRect;
+ Place curPlace;
+ Place state;
+ bool resizeEnabled : 1;
+ bool moveEnabled : 1;
+ bool nl : 1;
+ bool opaque : 1;
+ bool isToolbar : 1;
+ bool stretchable[3];
+ Qt::Orientation startOrientation;
+ int cMode;
+ QPoint startOffset;
+ int offs;
+ QSize fExtent;
+ Q3DockWindowResizeHandle *hHandleTop, *hHandleBottom, *vHandleLeft, *vHandleRight;
+ QVBoxLayout *hbox;
+ QHBoxLayout *vbox;
+ QBoxLayout *childBox;
+ void *dockWindowData;
+ QPoint lastPos;
+ QSize lastSize;
+ QWidgetResizeHandler *widgetResizeHandler;
+ Q3DockWindowPrivate *d;
+
+private:
+ Q_DISABLE_COPY(Q3DockWindow)
+};
+
+inline Q3DockArea *Q3DockWindow::area() const
+{
+ return dockArea;
+}
+
+#endif // QT_NO_MAINWINDOW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DOCKWINDOW_H
diff --git a/src/qt3support/widgets/q3frame.cpp b/src/qt3support/widgets/q3frame.cpp
new file mode 100644
index 0000000..7798d46
--- /dev/null
+++ b/src/qt3support/widgets/q3frame.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3frame.h"
+#include "qevent.h"
+#include "qpainter.h"
+
+QT_BEGIN_NAMESPACE
+
+/*! \class Q3Frame
+
+ \compat
+*/
+
+/*!
+ Creates a new frame with the given \a parent, object \a name, and
+ with widget flags \a f.
+*/
+Q3Frame::Q3Frame(QWidget* parent, const char* name, Qt::WindowFlags f)
+ :QFrame(parent, f), marg(0)
+{
+ if (name)
+ setObjectName(QLatin1String(name));
+ setAttribute(Qt::WA_LayoutOnEntireRect);
+}
+
+/*!
+ Destructs the frame.
+*/
+Q3Frame::~Q3Frame()
+{
+}
+
+/*!
+ Paints the frame (or part of the frame) that's necessary,
+ depending on the \a event.
+*/
+void Q3Frame::paintEvent(QPaintEvent * event)
+{
+ QPainter paint(this);
+ if (!contentsRect().contains(event->rect())) {
+ paint.save();
+ paint.setClipRegion(event->region().intersected(frameRect()));
+ drawFrame(&paint);
+ paint.restore();
+ }
+ if (event->rect().intersects(contentsRect())) {
+ paint.setClipRegion(event->region().intersected(contentsRect()));
+ drawContents(&paint);
+ }
+}
+
+/*!
+ \fn void Q3Frame::drawContents(QPainter *painter)
+
+ Virtual function that draws the contents of the frame on the given
+ \a painter.
+
+ The QPainter is already open when you get it, and you must leave
+ it open. Painter \link QPainter::setWorldMatrix()
+ transformations\endlink are switched off on entry. If you
+ transform the painter, remember to take the frame into account and
+ \link QPainter::resetXForm() reset transformation\endlink before
+ returning.
+
+ This function is reimplemented by subclasses that draw something
+ inside the frame. It should only draw inside contentsRect(). The
+ default function does nothing.
+
+ \sa contentsRect(), QPainter::setClipRect()
+*/
+
+void Q3Frame::drawContents(QPainter *)
+{
+}
+
+/*!
+ Draws the frame using the painter \a p and the current frame
+ attributes and color group. The rectangle inside the frame is not
+ affected.
+
+ This function is virtual, but in general you do not need to
+ reimplement it. If you do, note that the QPainter is already open
+ and must remain open.
+
+ \sa frameRect(), contentsRect(), drawContents(), frameStyle(), setPalette()
+*/
+
+void Q3Frame::drawFrame(QPainter *p)
+{
+ QFrame::drawFrame(p);
+}
+
+/*!
+ \fn void Q3Frame::resizeEvent(QResizeEvent *event)
+
+ This just calls frameChanged(); it does not make use of the \a
+ event itself.
+*/
+void Q3Frame::resizeEvent(QResizeEvent *e)
+{
+ if (e->size() == e->oldSize())
+ frameChanged();
+}
+
+/*!
+ Virtual function that is called when the frame style, line width
+ or mid-line width changes.
+
+ This function can be reimplemented by subclasses that need to know
+ when the frame attributes change.
+*/
+
+void Q3Frame::frameChanged()
+{
+}
+
+
+/*!
+ \property Q3Frame::margin
+ \brief the width of the margin
+
+ The margin is the distance between the innermost pixel of the
+ frame and the outermost pixel of contentsRect(). It is included in
+ frameWidth().
+
+ The margin is filled according to backgroundMode().
+
+ The default value is 0.
+
+ \sa lineWidth(), frameWidth()
+*/
+
+void Q3Frame::setMargin(int w)
+{
+ if (marg == w)
+ return;
+ marg = w;
+ update();
+ frameChanged();
+}
+
+/*!
+ \property Q3Frame::contentsRect
+ \brief the frame's contents rectangle (including the margins)
+*/
+QRect Q3Frame::contentsRect() const
+{
+ QRect cr(QFrame::contentsRect());
+ cr.adjust(marg, marg, -marg, -marg);
+ return cr;
+}
+
+/*!
+ Returns the width of the frame (including the margin).
+*/
+int Q3Frame::frameWidth() const
+{
+ return QFrame::frameWidth() + marg;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3frame.h b/src/qt3support/widgets/q3frame.h
new file mode 100644
index 0000000..05092b5
--- /dev/null
+++ b/src/qt3support/widgets/q3frame.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3FRAME_H
+#define Q3FRAME_H
+
+#include <QtGui/qframe.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3Frame : public QFrame
+{
+ Q_OBJECT
+ Q_PROPERTY(int margin READ margin WRITE setMargin)
+ Q_PROPERTY(QRect contentsRect READ contentsRect)
+
+public:
+ Q3Frame(QWidget* parent, const char* name = 0, Qt::WindowFlags f = 0);
+ ~Q3Frame();
+#ifndef qdoc
+ bool lineShapesOk() const { return true; }
+#endif
+
+ int margin() const { return marg; }
+ void setMargin(int);
+
+ QRect contentsRect() const;
+ int frameWidth() const;
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void resizeEvent(QResizeEvent *);
+
+ virtual void frameChanged();
+ virtual void drawFrame(QPainter *);
+ virtual void drawContents(QPainter *);
+
+private:
+ Q_DISABLE_COPY(Q3Frame)
+
+ int marg;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3FRAME_H
diff --git a/src/qt3support/widgets/q3grid.cpp b/src/qt3support/widgets/q3grid.cpp
new file mode 100644
index 0000000..46c12fb
--- /dev/null
+++ b/src/qt3support/widgets/q3grid.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3grid.h"
+#include "qlayout.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3Grid
+ \brief The Q3Grid widget provides simple geometry management of its children.
+
+ \compat
+
+ The grid places its widgets either in columns or in rows depending
+ on its orientation.
+
+ The number of rows \e or columns is defined in the constructor.
+ All the grid's children will be placed and sized in accordance
+ with their sizeHint() and sizePolicy().
+
+ Use setMargin() to add space around the grid itself, and
+ setSpacing() to add space between the widgets.
+
+ \sa Q3VBox Q3HBox QGridLayout
+*/
+
+/*!
+ \typedef Q3Grid::Direction
+ \internal
+*/
+
+/*!
+ Constructs a grid widget with parent \a parent, called \a name.
+ If \a orient is \c Horizontal, \a n specifies the number of
+ columns. If \a orient is \c Vertical, \a n specifies the number of
+ rows. The widget flags \a f are passed to the Q3Frame constructor.
+*/
+Q3Grid::Q3Grid(int n, Qt::Orientation orient, QWidget *parent, const char *name,
+ Qt::WindowFlags f)
+ : Q3Frame(parent, name, f)
+{
+ int nCols, nRows;
+ if (orient == Qt::Horizontal) {
+ nCols = n;
+ nRows = -1;
+ } else {
+ nCols = -1;
+ nRows = n;
+ }
+ (new QGridLayout(this, nRows, nCols, 0, 0, name))->setAutoAdd(true);
+}
+
+
+
+/*!
+ Constructs a grid widget with parent \a parent, called \a name.
+ \a n specifies the number of columns. The widget flags \a f are
+ passed to the Q3Frame constructor.
+ */
+Q3Grid::Q3Grid(int n, QWidget *parent, const char *name, Qt::WindowFlags f)
+ : Q3Frame(parent, name, f)
+{
+ (new QGridLayout(this, -1, n, 0, 0, name))->setAutoAdd(true);
+}
+
+
+/*!
+ Sets the spacing between the child widgets to \a space.
+*/
+
+void Q3Grid::setSpacing(int space)
+{
+ if (layout())
+ layout()->setSpacing(space);
+}
+
+
+/*!\reimp
+ */
+void Q3Grid::frameChanged()
+{
+ if (layout())
+ layout()->setMargin(frameWidth());
+}
+
+
+/*!
+ \reimp
+*/
+
+QSize Q3Grid::sizeHint() const
+{
+ QWidget *mThis = (QWidget*)this;
+ QApplication::sendPostedEvents(mThis, QEvent::ChildInserted);
+ return Q3Frame::sizeHint();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3grid.h b/src/qt3support/widgets/q3grid.h
new file mode 100644
index 0000000..544145d
--- /dev/null
+++ b/src/qt3support/widgets/q3grid.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GRID_H
+#define Q3GRID_H
+
+#include <Qt3Support/q3frame.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QGridLayout;
+
+class Q_COMPAT_EXPORT Q3Grid : public Q3Frame
+{
+ Q_OBJECT
+public:
+ Q3Grid(int n, QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0);
+ Q3Grid(int n, Qt::Orientation orient, QWidget* parent=0, const char* name=0,
+ Qt::WindowFlags f = 0);
+
+ void setSpacing(int);
+ QSize sizeHint() const;
+
+ typedef Qt::Orientation Direction;
+
+protected:
+ void frameChanged();
+
+private:
+ Q_DISABLE_COPY(Q3Grid)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GRID_H
diff --git a/src/qt3support/widgets/q3gridview.cpp b/src/qt3support/widgets/q3gridview.cpp
new file mode 100644
index 0000000..7de2f45
--- /dev/null
+++ b/src/qt3support/widgets/q3gridview.cpp
@@ -0,0 +1,367 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "q3gridview.h"
+#include "qpainter.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+/*!
+ \class Q3GridView
+ \brief The Q3GridView class provides an abstract base for
+ fixed-size grids.
+
+ \compat
+
+ A grid view consists of a number of abstract cells organized in
+ rows and columns. The cells have a fixed size and are identified
+ with a row index and a column index. The top-left cell is in row
+ 0, column 0. The bottom-right cell is in row numRows()-1, column
+ numCols()-1.
+
+ You can define \l{Q3GridView::numRows} {numRows},
+ \l{Q3GridView::numCols} {numCols}, \l{Q3GridView::cellWidth}
+ {cellWidth} and \l{Q3GridView::cellHeight} {cellHeight}. Reimplement
+ the pure virtual function paintCell() to draw the contents of a
+ cell.
+
+ With ensureCellVisible(), you can ensure a certain cell is
+ visible. With rowAt() and columnAt() you can find a cell based on
+ the given x- and y-coordinates.
+
+ If you need to monitor changes to the grid's dimensions (i.e. when
+ numRows or numCols is changed), reimplement the dimensionChange()
+ change handler.
+
+ Note: the row and column indices are always given in the order,
+ row (vertical offset) then column (horizontal offset). This order
+ is the opposite of all pixel operations, which are given in the
+ order x (horizontal offset), y (vertical offset).
+
+ Q3GridView is a very simple abstract class based on Q3ScrollView. It
+ is designed to simplify the task of drawing many cells of the same
+ size in a potentially scrollable canvas. If you need rows and
+ columns with different sizes, use a Q3Table instead. If you need a
+ simple list of items, use a Q3ListBox. If you need to present
+ hierarichal data use a Q3ListView, and if you need random objects
+ at random positions, consider using either a Q3IconView or a
+ Q3Canvas.
+*/
+
+
+/*!
+ Constructs a grid view.
+
+ The \a parent, \a name and widget flag, \a f, arguments are passed
+ to the Q3ScrollView constructor.
+*/
+Q3GridView::Q3GridView(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : Q3ScrollView(parent, name, f | WStaticContents),
+ nrows(5), ncols(5), cellw(12), cellh(12)
+{
+ viewport()->setBackgroundMode(PaletteBase);
+ setBackgroundMode(PaletteBackground, PaletteBase);
+ viewport()->setFocusProxy(this);
+}
+
+/*!
+ Destroys the grid view.
+*/
+Q3GridView::~Q3GridView()
+{
+}
+
+void Q3GridView::updateGrid()
+{
+ resizeContents(ncols * cellw, nrows * cellh);
+}
+
+/*!
+ \property Q3GridView::numRows
+ \brief The number of rows in the grid
+
+ \sa numCols
+*/
+void Q3GridView::setNumRows(int numRows)
+{
+ int oldnrows = nrows;
+ nrows = numRows;
+ dimensionChange(oldnrows, ncols);
+ updateGrid();
+}
+
+/*!
+ \property Q3GridView::numCols
+ \brief The number of columns in the grid
+
+ \sa numRows
+*/
+void Q3GridView::setNumCols(int numCols)
+{
+ int oldncols = ncols;
+ ncols = numCols;
+ dimensionChange(nrows, oldncols);
+ updateGrid();
+}
+
+/*!
+ \property Q3GridView::cellWidth
+ \brief The width of a grid column
+
+ All columns in a grid view have the same width.
+
+ \sa cellHeight
+*/
+void Q3GridView::setCellWidth(int cellWidth)
+{
+ cellw = cellWidth;
+ updateGrid();
+ updateContents();
+}
+
+/*!
+ \property Q3GridView::cellHeight
+ \brief The height of a grid row
+
+ All rows in a grid view have the same height.
+
+ \sa cellWidth
+*/
+void Q3GridView::setCellHeight(int cellHeight)
+{
+ cellh = cellHeight;
+ updateGrid();
+ updateContents();
+}
+
+/*!
+ Returns the geometry of cell (\a row, \a column) in the content
+ coordinate system.
+
+ \sa cellRect()
+*/
+QRect Q3GridView::cellGeometry(int row, int column)
+{
+ QRect r;
+ if (row >= 0 && row < nrows && column >= 0 && column < ncols)
+ r.setRect(cellw * column, cellh * row, cellw, cellh);
+ return r;
+}
+
+/*!
+ Repaints cell (\a row, \a column).
+
+ If \a erase is true, Qt erases the area of the cell before the
+ paintCell() call; otherwise no erasing takes place.
+
+ \sa QWidget::repaint()
+*/
+void Q3GridView::repaintCell(int row, int column, bool erase)
+{
+ repaintContents(cellGeometry(row, column), erase);
+}
+
+/*!
+ Updates cell (\a row, \a column).
+
+ \sa QWidget::update()
+*/
+void Q3GridView::updateCell(int row, int column)
+{
+ updateContents(cellGeometry(row, column));
+}
+
+/*!
+ Ensures cell (\a row, \a column) is visible, scrolling the grid
+ view if necessary.
+*/
+void Q3GridView::ensureCellVisible(int row, int column)
+{
+ QRect r = cellGeometry(row, column);
+ ensureVisible(r.x(), r.y(), r.width(), r.height());
+}
+
+/*!
+ This function fills the \a cw pixels wide and \a ch pixels high
+ rectangle starting at position (\a cx, \a cy) with the background
+ color using the painter \a p.
+
+ paintEmptyArea() is invoked by drawContents() to erase or fill
+ unused areas.
+*/
+
+void Q3GridView::paintEmptyArea(QPainter *p, int cx ,int cy, int cw, int ch)
+{
+ if (gridSize().width() >= contentsWidth() && gridSize().height() >= contentsHeight())
+ return;
+ // Region of the rect we should draw
+ contentsToViewport(cx, cy, cx, cy);
+ QRegion reg(QRect(cx, cy, cw, ch));
+ // Subtract the table from it
+ reg = reg.subtracted(QRect(contentsToViewport(QPoint(0, 0)), gridSize()));
+
+ // And draw the rectangles (transformed as needed)
+ QVector<QRect> r = reg.rects();
+ const QBrush &brush = backgroundBrush();
+ for (int i = 0; i < (int)r.count(); ++i)
+ p->fillRect(r[ i ], brush);
+}
+
+/*!\reimp
+ */
+void Q3GridView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ int colfirst = columnAt(cx);
+ int collast = columnAt(cx + cw);
+ int rowfirst = rowAt(cy);
+ int rowlast = rowAt(cy + ch);
+
+ if (rowfirst == -1 || colfirst == -1) {
+ paintEmptyArea(p, cx, cy, cw, ch);
+ return;
+ }
+
+ if (collast < 0 || collast >= ncols)
+ collast = ncols-1;
+ if (rowlast < 0 || rowlast >= nrows)
+ rowlast = nrows-1;
+
+ // Go through the rows
+ for (int r = rowfirst; r <= rowlast; ++r) {
+ // get row position and height
+ int rowp = r * cellh;
+
+ // Go through the columns in the row r
+ // if we know from where to where, go through [colfirst, collast],
+ // else go through all of them
+ for (int c = colfirst; c <= collast; ++c) {
+ // get position and width of column c
+ int colp = c * cellw;
+ // Translate painter and draw the cell
+ p->translate(colp, rowp);
+ paintCell(p, r, c);
+ p->translate(-colp, -rowp);
+ }
+ }
+
+ // Paint empty rects
+ paintEmptyArea(p, cx, cy, cw, ch);
+}
+
+/*!
+ \reimp
+
+ (Implemented to get rid of a compiler warning.)
+*/
+void Q3GridView::drawContents(QPainter *)
+{
+}
+
+/*!
+ \fn void Q3GridView::dimensionChange(int oldNumRows, int oldNumCols)
+
+ This change handler is called whenever any of the grid's
+ dimensions change. \a oldNumRows and \a oldNumCols contain the
+ old dimensions, numRows() and numCols() contain the new
+ dimensions.
+*/
+void Q3GridView::dimensionChange(int, int) {}
+
+
+
+/*!
+ \fn int Q3GridView::rowAt(int y) const
+
+ Returns the number of the row at position \a y. \a y must be given
+ in content coordinates.
+
+ \sa columnAt()
+*/
+
+/*!
+ \fn int Q3GridView::columnAt(int x) const
+
+ Returns the number of the column at position \a x. \a x must be
+ given in content coordinates.
+
+ \sa rowAt()
+*/
+
+/*!
+ \fn void Q3GridView::paintCell(QPainter *p, int row, int col)
+
+ This pure virtual function is called to paint the single cell at
+ (\a row, \a col) using painter \a p. The painter must be open when
+ paintCell() is called and must remain open.
+
+ The coordinate system is \link QPainter::translate() translated
+ \endlink so that the origin is at the top-left corner of the cell
+ to be painted, i.e. \e cell coordinates. Do not scale or shear
+ the coordinate system (or if you do, restore the transformation
+ matrix before you return).
+
+ The painter is not clipped by default in order to get maximum
+ efficiency. If you want clipping, use
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3gridview.cpp 0
+*/
+
+/*!
+ \fn QRect Q3GridView::cellRect() const
+
+ Returns the geometry of a cell in a cell's coordinate system. This
+ is a convenience function useful in paintCell(). It is equivalent
+ to QRect(0, 0, cellWidth(), cellHeight()).
+
+ \sa cellGeometry()
+
+*/
+
+/*!
+ \fn QSize Q3GridView::gridSize() const
+
+ Returns the size of the grid in pixels.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3gridview.h b/src/qt3support/widgets/q3gridview.h
new file mode 100644
index 0000000..a46c6fc
--- /dev/null
+++ b/src/qt3support/widgets/q3gridview.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GRIDVIEW_H
+#define Q3GRIDVIEW_H
+
+#include <Qt3Support/q3scrollview.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3GridViewPrivate;
+
+class Q_COMPAT_EXPORT Q3GridView : public Q3ScrollView
+{
+ Q_OBJECT
+ Q_PROPERTY(int numRows READ numRows WRITE setNumRows)
+ Q_PROPERTY(int numCols READ numCols WRITE setNumCols)
+ Q_PROPERTY(int cellWidth READ cellWidth WRITE setCellWidth)
+ Q_PROPERTY(int cellHeight READ cellHeight WRITE setCellHeight)
+public:
+
+ Q3GridView(QWidget *parent=0, const char *name=0, Qt::WindowFlags f=0);
+ ~Q3GridView();
+
+ int numRows() const;
+ virtual void setNumRows(int);
+ int numCols() const;
+ virtual void setNumCols(int);
+
+ int cellWidth() const;
+ virtual void setCellWidth(int);
+ int cellHeight() const;
+ virtual void setCellHeight(int);
+
+ QRect cellRect() const;
+ QRect cellGeometry(int row, int column);
+ QSize gridSize() const;
+
+ int rowAt(int y) const;
+ int columnAt(int x) const;
+
+ void repaintCell(int row, int column, bool erase=true);
+ void updateCell(int row, int column);
+ void ensureCellVisible(int row, int column);
+
+protected:
+ virtual void paintCell(QPainter *, int row, int col) = 0;
+ virtual void paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch);
+
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+
+ virtual void dimensionChange(int, int);
+
+private:
+ void drawContents(QPainter*);
+ void updateGrid();
+
+ int nrows;
+ int ncols;
+ int cellw;
+ int cellh;
+ Q3GridViewPrivate* d;
+
+ Q_DISABLE_COPY(Q3GridView)
+};
+
+inline int Q3GridView::cellWidth() const
+{ return cellw; }
+
+inline int Q3GridView::cellHeight() const
+{ return cellh; }
+
+inline int Q3GridView::rowAt(int y) const
+{ return y / cellh; }
+
+inline int Q3GridView::columnAt(int x) const
+{ return x / cellw; }
+
+inline int Q3GridView::numRows() const
+{ return nrows; }
+
+inline int Q3GridView::numCols() const
+{return ncols; }
+
+inline QRect Q3GridView::cellRect() const
+{ return QRect(0, 0, cellw, cellh); }
+
+inline QSize Q3GridView::gridSize() const
+{ return QSize(ncols * cellw, nrows * cellh); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GRIDVIEW_H
diff --git a/src/qt3support/widgets/q3groupbox.cpp b/src/qt3support/widgets/q3groupbox.cpp
new file mode 100644
index 0000000..b4414fb
--- /dev/null
+++ b/src/qt3support/widgets/q3groupbox.cpp
@@ -0,0 +1,964 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3groupbox.h"
+
+#include "qlayout.h"
+#include "qpainter.h"
+#include "qbitmap.h"
+#include "q3accel.h"
+#include "qradiobutton.h"
+#include "qdrawutil.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#include "qcheckbox.h"
+#include "qaccessible.h"
+#include "qstyleoption.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3GroupBox
+ \brief The Q3GroupBox widget provides a group box frame with a title.
+
+ \compat
+
+ A group box provides a frame, a title and a keyboard shortcut, and
+ displays various other widgets inside itself. The title is on top,
+ the keyboard shortcut moves keyboard focus to one of the group
+ box's child widgets, and the child widgets are usually laid out
+ horizontally (or vertically) inside the frame.
+
+ The simplest way to use it is to create a group box with the
+ desired number of columns (or rows) and orientation, and then just
+ create widgets with the group box as parent.
+
+ It is also possible to change the orientation() and number of
+ columns() after construction, or to ignore all the automatic
+ layout support and manage the layout yourself. You can add 'empty'
+ spaces to the group box with addSpace().
+
+ Q3GroupBox also lets you set the title() (normally set in the
+ constructor) and the title's alignment().
+
+ You can change the spacing used by the group box with
+ setInsideMargin() and setInsideSpacing(). To minimize space
+ consumption, you can remove the right, left and bottom edges of
+ the frame with setFlat().
+
+ \sa QButtonGroup
+*/
+
+class QCheckBox;
+
+class Q3GroupBoxPrivate
+{
+public:
+ Q3GroupBoxPrivate(Q3GroupBox *w):
+ q(w), vbox(0), grid(0), row(0), col(0), nRows(0), nCols(0), dir(Qt::Horizontal),
+ spac(5), marg(11),
+ checkbox(0),
+ frameStyle(Q3GroupBox::GroupBoxPanel | Q3GroupBox::Sunken),
+ lineWidth(1), midLineWidth(0), frameWidth(0),
+ leftFrameWidth(0), rightFrameWidth(0),
+ topFrameWidth(0), bottomFrameWidth(0) {}
+
+ void updateFrameWidth();
+ void updateStyledFrameWidths();
+
+ Q3GroupBox *q;
+ QVBoxLayout *vbox;
+ QGridLayout *grid;
+ int row;
+ int col;
+ int nRows, nCols;
+ Qt::Orientation dir;
+ int spac, marg;
+
+ QCheckBox *checkbox;
+
+ int frameStyle;
+ int oldFrameStyle;
+ short lineWidth, //line width
+ midLineWidth; //midline width
+ int frameWidth;
+ short leftFrameWidth, rightFrameWidth,
+ topFrameWidth, bottomFrameWidth;
+};
+
+/*!
+ \internal
+ Updates the frame widths from the style.
+*/
+void Q3GroupBoxPrivate::updateStyledFrameWidths()
+{
+ QStyleOptionFrameV2 opt;
+ opt.initFrom(q);
+ QRect cr = q->style()->subElementRect(QStyle::SE_FrameContents, &opt, q);
+ leftFrameWidth = cr.left() - opt.rect.left();
+ topFrameWidth = cr.top() - opt.rect.top();
+ rightFrameWidth = opt.rect.right() - cr.right(),
+ bottomFrameWidth = opt.rect.bottom() - cr.bottom();
+ frameWidth = qMax(qMax(leftFrameWidth, rightFrameWidth),
+ qMax(topFrameWidth, bottomFrameWidth));
+}
+
+/*!
+ \internal
+ Updated the frameWidth parameter.
+*/
+
+void Q3GroupBoxPrivate::updateFrameWidth()
+{
+ QRect fr = q->frameRect();
+
+ int frameShape = frameStyle & QFrame::Shape_Mask;
+ int frameShadow = frameStyle & QFrame::Shadow_Mask;
+
+ frameWidth = -1;
+
+ switch (frameShape) {
+
+ case QFrame::NoFrame:
+ frameWidth = 0;
+ break;
+
+ case QFrame::Box:
+ case QFrame::HLine:
+ case QFrame::VLine:
+ switch (frameShadow) {
+ case QFrame::Plain:
+ frameWidth = lineWidth;
+ break;
+ case QFrame::Raised:
+ case QFrame::Sunken:
+ frameWidth = (short)(lineWidth*2 + midLineWidth);
+ break;
+ }
+ break;
+
+ case QFrame::StyledPanel:
+ updateStyledFrameWidths();
+ break;
+
+ case QFrame::WinPanel:
+ frameWidth = 2;
+ break;
+
+
+ case QFrame::Panel:
+ switch (frameShadow) {
+ case QFrame::Plain:
+ case QFrame::Raised:
+ case QFrame::Sunken:
+ frameWidth = lineWidth;
+ break;
+ }
+ break;
+ }
+
+ if (frameWidth == -1) // invalid style
+ frameWidth = 0;
+
+ q->setFrameRect(fr);
+}
+
+
+
+
+
+/*!
+ Constructs a group box widget with no title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+
+ This constructor does not do automatic layout.
+*/
+
+Q3GroupBox::Q3GroupBox(QWidget *parent, const char *name)
+ : QGroupBox(parent, name)
+{
+ init();
+}
+
+/*!
+ Constructs a group box with the title \a title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+
+ This constructor does not do automatic layout.
+*/
+
+Q3GroupBox::Q3GroupBox(const QString &title, QWidget *parent, const char *name)
+ : QGroupBox(parent, name)
+{
+ init();
+ setTitle(title);
+}
+
+/*!
+ Constructs a group box with no title. Child widgets will be
+ arranged in \a strips rows or columns (depending on \a
+ orientation).
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3GroupBox::Q3GroupBox(int strips, Qt::Orientation orientation,
+ QWidget *parent, const char *name)
+ : QGroupBox(parent, name)
+{
+ init();
+ setColumnLayout(strips, orientation);
+}
+
+/*!
+ Constructs a group box titled \a title. Child widgets will be
+ arranged in \a strips rows or columns (depending on \a
+ orientation).
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3GroupBox::Q3GroupBox(int strips, Qt::Orientation orientation,
+ const QString &title, QWidget *parent,
+ const char *name)
+ : QGroupBox(parent, name)
+{
+ init();
+ setTitle(title);
+ setColumnLayout(strips, orientation);
+}
+
+/*!
+ Destroys the group box.
+*/
+Q3GroupBox::~Q3GroupBox()
+{
+ delete d;
+}
+
+void Q3GroupBox::init()
+{
+ d = new Q3GroupBoxPrivate(this);
+}
+
+
+/*! \reimp
+*/
+void Q3GroupBox::resizeEvent(QResizeEvent *e)
+{
+ QGroupBox::resizeEvent(e);
+}
+
+
+/*!
+ Adds an empty cell at the next free position. If \a size is
+ greater than 0, the empty cell takes \a size to be its fixed width
+ (if orientation() is \c Horizontal) or height (if orientation() is
+ \c Vertical).
+
+ Use this method to separate the widgets in the group box or to
+ skip the next free cell. For performance reasons, call this method
+ after calling setColumnLayout() or by changing the \l
+ Q3GroupBox::columns or \l Q3GroupBox::orientation properties. It is
+ generally a good idea to call these methods first (if needed at
+ all), and insert the widgets and spaces afterwards.
+*/
+void Q3GroupBox::addSpace(int size)
+{
+ QApplication::sendPostedEvents(this, QEvent::ChildInserted);
+
+ if (d->nCols <= 0 || d->nRows <= 0)
+ return;
+
+ if (d->row >= d->nRows || d->col >= d->nCols)
+ d->grid->expand(d->row+1, d->col+1);
+
+ if (size > 0) {
+ QSpacerItem *spacer
+ = new QSpacerItem((d->dir == Qt::Horizontal) ? 0 : size,
+ (d->dir == Qt::Vertical) ? 0 : size,
+ QSizePolicy::Fixed, QSizePolicy::Fixed);
+ d->grid->addItem(spacer, d->row, d->col);
+ }
+
+ skip();
+}
+
+/*!
+ \property Q3GroupBox::columns
+ \brief the number of columns or rows (depending on \l Q3GroupBox::orientation) in the group box
+
+ Usually it is not a good idea to set this property because it is
+ slow (it does a complete layout). It is best to set the number
+ of columns directly in the constructor.
+*/
+int Q3GroupBox::columns() const
+{
+ if (d->dir == Qt::Horizontal)
+ return d->nCols;
+ return d->nRows;
+}
+
+void Q3GroupBox::setColumns(int c)
+{
+ setColumnLayout(c, d->dir);
+}
+
+/*!
+ Returns the width of the empty space between the items in the
+ group and the frame of the group.
+
+ Only applies if the group box has a defined orientation.
+
+ The default is usually 11, by may vary depending on the platform
+ and style.
+
+ \sa setInsideMargin(), orientation
+*/
+int Q3GroupBox::insideMargin() const
+{
+ return d->marg;
+}
+
+/*!
+ Returns the width of the empty space between each of the items
+ in the group.
+
+ Only applies if the group box has a defined orientation.
+
+ The default is usually 5, by may vary depending on the platform
+ and style.
+
+ \sa setInsideSpacing(), orientation
+*/
+int Q3GroupBox::insideSpacing() const
+{
+ return d->spac;
+}
+
+/*!
+ Sets the width of the inside margin to \a m pixels.
+
+ \sa insideMargin()
+*/
+void Q3GroupBox::setInsideMargin(int m)
+{
+ d->marg = m;
+ setColumnLayout(columns(), d->dir);
+}
+
+/*!
+ Sets the width of the empty space between each of the items in
+ the group to \a s pixels.
+
+ \sa insideSpacing()
+*/
+void Q3GroupBox::setInsideSpacing(int s)
+{
+ d->spac = s;
+ setColumnLayout(columns(), d->dir);
+}
+
+/*!
+ \property Q3GroupBox::orientation
+ \brief the group box's orientation
+
+ A horizontal group box arranges its children in columns, while a
+ vertical group box arranges them in rows.
+
+ Usually it is not a good idea to set this property because it is
+ slow (it does a complete layout). It is better to set the
+ orientation directly in the constructor.
+*/
+void Q3GroupBox::setOrientation(Qt::Orientation o)
+{
+ setColumnLayout(columns(), o);
+}
+
+
+Qt::Orientation Q3GroupBox::orientation() const
+{
+ return d->dir;
+}
+
+/*!
+ Changes the layout of the group box. This function is only useful
+ in combination with the default constructor that does not take any
+ layout information. This function will put all existing children
+ in the new layout. It is not good Qt programming style to call
+ this function after children have been inserted. Sets the number
+ of columns or rows to be \a strips, depending on \a direction.
+
+ \sa orientation columns
+*/
+void Q3GroupBox::setColumnLayout(int strips, Qt::Orientation direction)
+{
+ if (layout())
+ delete layout();
+
+ d->vbox = 0;
+ d->grid = 0;
+
+ if (strips < 0) // if 0, we create the d->vbox but not the d->grid. See below.
+ return;
+
+ d->vbox = new QVBoxLayout(this, d->marg, 0);
+
+ d->nCols = 0;
+ d->nRows = 0;
+ d->dir = direction;
+
+ // Send all child events and ignore them. Otherwise we will end up
+ // with doubled insertion. This won't do anything because d->nCols ==
+ // d->nRows == 0.
+ QApplication::sendPostedEvents(this, QEvent::ChildInserted);
+
+ // if 0 or smaller , create a vbox-layout but no grid. This allows
+ // the designer to handle its own grid layout in a group box.
+ if (strips <= 0)
+ return;
+
+ d->dir = direction;
+ if (d->dir == Qt::Horizontal) {
+ d->nCols = strips;
+ d->nRows = 1;
+ } else {
+ d->nCols = 1;
+ d->nRows = strips;
+ }
+ d->grid = new QGridLayout(d->nRows, d->nCols, d->spac);
+ d->row = d->col = 0;
+ d->grid->setAlignment(Qt::AlignTop);
+ d->vbox->addLayout(d->grid);
+
+ // Add all children
+ QObjectList childList = children();
+ if (!childList.isEmpty()) {
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *o = childList.at(i);
+ if (o->isWidgetType() && o != d->checkbox)
+ insertWid(static_cast<QWidget *>(o));
+ }
+ }
+}
+
+/*!\reimp */
+void Q3GroupBox::childEvent(QChildEvent *c)
+{
+ QGroupBox::childEvent(c);
+ if (!c->inserted() || !c->child()->isWidgetType())
+ return;
+ if (d->grid) {
+ insertWid((QWidget*)c->child());
+ }
+}
+
+void Q3GroupBox::insertWid(QWidget* w)
+{
+ if (d->row >= d->nRows || d->col >= d->nCols)
+ d->grid->expand(d->row+1, d->col+1);
+ d->grid->addWidget(w, d->row, d->col);
+ skip();
+}
+
+
+void Q3GroupBox::skip()
+{
+ // Same as QGrid::skip()
+ if (d->dir == Qt::Horizontal) {
+ if (d->col+1 < d->nCols) {
+ d->col++;
+ } else {
+ d->col = 0;
+ d->row++;
+ }
+ } else { //Vertical
+ if (d->row+1 < d->nRows) {
+ d->row++;
+ } else {
+ d->row = 0;
+ d->col++;
+ }
+ }
+}
+
+
+/*! \reimp */
+void Q3GroupBox::changeEvent(QEvent *ev)
+{
+ QGroupBox::changeEvent(ev);
+}
+
+/*! \reimp */
+bool Q3GroupBox::event(QEvent *e)
+{
+ if (e->type()==QEvent::Paint)
+ {
+ QStyleOptionGroupBox opt;
+ initStyleOption(&opt);
+ opt.lineWidth=d->lineWidth;
+ opt.midLineWidth=d->midLineWidth;
+ QPainter p(this);
+ if (frameShape()==GroupBoxPanel)
+ {
+ style()->drawComplexControl(QStyle::CC_GroupBox, &opt, &p, this);
+ }
+ else {
+ //in case it is a Paint event with a frame shape different from the group box
+ const QRect textRect = style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxLabel, this);
+ const QRect checkBoxRect = style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxCheckBox, this);
+
+ // Draw title
+ if ((opt.subControls & QStyle::SC_GroupBoxLabel) && !opt.text.isEmpty()) {
+ QColor textColor = opt.textColor;
+ if (textColor.isValid())
+ p.setPen(textColor);
+ int alignment = int(opt.textAlignment);
+ if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
+ alignment |= Qt::TextHideMnemonic;
+
+ style()->drawItemText(&p, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
+ opt.palette, opt.state & QStyle::State_Enabled, opt.text,
+ textColor.isValid() ? QPalette::NoRole : QPalette::WindowText);
+
+ if (opt.state & QStyle::State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(opt);
+ fropt.rect = textRect;
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, &p, this);
+ }
+ }
+
+ // Draw checkbox
+ if (opt.subControls & QStyle::SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(opt);
+ box.rect = checkBoxRect;
+ style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &box, &p, this);
+ }
+
+ //sets clipping
+ QRegion region(rect());
+ if (!title().isEmpty()) {
+ bool ltr = layoutDirection() == Qt::LeftToRight;
+ QRect finalRect = checkBoxRect.united(textRect);
+ if (isCheckable())
+ finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+ region -= finalRect;
+ }
+ p.setClipRegion(region);
+
+ drawFrame(&p);
+ }
+ return false;
+ }
+ return QGroupBox::event(e);
+}
+
+/*!
+ \fn void Q3GroupBox::drawFrame(QPainter *p)
+ \internal
+*/
+
+void Q3GroupBox::drawFrame(QPainter *p)
+{
+ QPoint p1, p2;
+ QStyleOptionFrame opt;
+ opt.init(this);
+
+ int frameShape = d->frameStyle & QFrame::Shape_Mask;
+ int frameShadow = d->frameStyle & QFrame::Shadow_Mask;
+
+ int lw = 0;
+ int mlw = 0;
+ opt.rect = frameRect();
+
+ switch (frameShape) {
+ case QFrame::Box:
+ case QFrame::HLine:
+ case QFrame::VLine:
+ case QFrame::StyledPanel:
+ lw = d->lineWidth;
+ mlw = d->midLineWidth;
+ break;
+ default:
+ // most frame styles do not handle customized line and midline widths
+ // (see updateFrameWidth()).
+ lw = d->frameWidth;
+ break;
+ }
+ opt.lineWidth = lw;
+ opt.midLineWidth = mlw;
+ if (frameShadow == Sunken)
+ opt.state |= QStyle::State_Sunken;
+ else if (frameShadow == Raised)
+ opt.state |= QStyle::State_Raised;
+
+ switch (frameShape) {
+ case Box:
+ if (frameShadow == Plain)
+ qDrawPlainRect(p, opt.rect, opt.palette.foreground().color(), lw);
+ else
+ qDrawShadeRect(p, opt.rect, opt.palette, frameShadow == Sunken, lw, mlw);
+ break;
+
+ case StyledPanel:
+ style()->drawPrimitive(QStyle::PE_Frame, &opt, p, this);
+ break;
+
+ case Panel:
+ if (frameShadow == Plain)
+ qDrawPlainRect(p, opt.rect, opt.palette.foreground().color(), lw);
+ else
+ qDrawShadePanel(p, opt.rect, opt.palette, frameShadow == Sunken, lw);
+ break;
+
+ case WinPanel:
+ if (frameShadow == Plain)
+ qDrawPlainRect(p, opt.rect, opt.palette.foreground().color(), lw);
+ else
+ qDrawWinPanel(p, opt.rect, opt.palette, frameShadow == Sunken);
+ break;
+ case HLine:
+ case VLine:
+ if (frameShape == HLine) {
+ p1 = QPoint(opt.rect.x(), opt.rect.height() / 2);
+ p2 = QPoint(opt.rect.x() + opt.rect.width(), p1.y());
+ } else {
+ p1 = QPoint(opt.rect.x()+opt.rect.width() / 2, 0);
+ p2 = QPoint(p1.x(), opt.rect.height());
+ }
+ if (frameShadow == Plain) {
+ QPen oldPen = p->pen();
+ p->setPen(QPen(opt.palette.foreground().color(), lw));
+ p->drawLine(p1, p2);
+ p->setPen(oldPen);
+ } else {
+ qDrawShadeLine(p, p1, p2, opt.palette, frameShadow == Sunken, lw, mlw);
+ }
+ break;
+ }
+
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && hasFocus()) {
+ QStyleOptionFocusRect fopt;
+ fopt.init(this);
+ fopt.state |= QStyle::State_KeyboardFocusChange;
+ fopt.rect = frameRect();
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fopt, p, this);
+ }
+#endif
+}
+
+/*!
+ \property Q3GroupBox::frameShadow
+ \brief the frame shadow value from the frame style
+
+ \sa frameStyle()
+*/
+
+/*
+ \enum Q3GroupBox::FrameShape
+
+ This enum defines the available frame shapes a group box can
+ have. All values have equivalents in QFrame.
+
+ \value Box QFrame::Box
+ \value Sunken QFrame::Sunken
+ \value Plain QFrame::Plain
+ \value Raised QFrame::Raised
+ \value MShadow QFrame::Shadow_Mask
+ \value NoFrame QFrame::NoFrame
+ \value Panel QFrame::Panel
+ \value StyledPanel QFrame::StyledPanel
+ \value HLine QFrame::HLine
+ \value VLine QFrame::VLine
+ \value WinPanel QFrame::WinPanel
+ \value ToolBarPanel QFrame::StyledPanel
+ \value MenuBarPanel = QFrame::StyledPanel
+ \value PopupPanel QFrame::StyledPanel
+ \value LineEditPanel QFrame::StyledPanel
+ \value TabWidgetPanel QFrame::StyledPanel
+ \value GroupBoxPanel 0x0007
+ \value MShape QFrame::Shape_Mask
+*/
+
+
+void Q3GroupBox::setFrameShadow(DummyFrame s)
+{
+ setFrameStyle((d->frameStyle & MShape) | s);
+}
+
+Q3GroupBox::DummyFrame Q3GroupBox::frameShadow() const
+{
+ return (DummyFrame) (d->frameStyle & MShadow);
+}
+
+/*!
+ \property Q3GroupBox::frameShape
+ \brief the frame shape value from the frame style
+
+ \sa frameStyle(), frameShadow()
+*/
+
+void Q3GroupBox::setFrameShape(DummyFrame s)
+{
+ setFrameStyle((d->frameStyle & MShadow) | s);
+}
+
+Q3GroupBox::DummyFrame Q3GroupBox::frameShape() const
+{
+ return (DummyFrame) (d->frameStyle & MShape);
+}
+
+/*!
+ \fn void Q3GroupBox::setFrameStyle(int style)
+
+ Sets the frame style to \a style. The style is the bitwise OR
+ between a frame shape and a frame shadow style.
+*/
+
+void Q3GroupBox::setFrameStyle(int style)
+{
+ if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
+ switch (style & MShape) {
+ case HLine:
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ break;
+ case VLine:
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ break;
+ default:
+ if ((d->frameStyle & MShape) == HLine || (d->frameStyle & MShape) == VLine)
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ }
+ setAttribute(Qt::WA_WState_OwnSizePolicy, false);
+ }
+ d->frameStyle = style;
+ update();
+ d->updateFrameWidth();
+ d->oldFrameStyle = style;
+}
+
+/*!
+ \fn int Q3GroupBox::frameStyle() const
+
+ Returns the frame style.
+*/
+
+int Q3GroupBox::frameStyle() const
+{
+ return d->frameStyle;
+}
+
+/*!
+ \property Q3GroupBox::lineWidth
+ \brief This property holds the width of the line.
+
+ \sa frameStyle(), frameShadow()
+*/
+
+void Q3GroupBox::setLineWidth(int w)
+{
+ if (short(w) == d->lineWidth)
+ return;
+ d->lineWidth = short(w);
+ d->updateFrameWidth();
+}
+
+int Q3GroupBox::lineWidth() const
+{
+ return d->lineWidth;
+}
+
+/*!
+ \property Q3GroupBox::midLineWidth
+ \brief This property holds the width of the mid-line.
+
+ \sa frameStyle(), frameShadow()
+*/
+
+void Q3GroupBox::setMidLineWidth(int w)
+{
+ if (short(w) == d->midLineWidth)
+ return;
+ d->midLineWidth = short(w);
+ d->updateFrameWidth();
+}
+
+int Q3GroupBox::midLineWidth() const
+{
+ return d->midLineWidth;
+}
+
+/*!
+ \property Q3GroupBox::frameRect
+ \brief the bounding rectangle of the frame of the group box.
+*/
+
+/*!
+ \fn QRect Q3GroupBox::frameRect() const
+ \internal
+*/
+
+QRect Q3GroupBox::frameRect() const
+{
+ QStyleOptionGroupBox opt;
+ initStyleOption(&opt);
+ QRect fr = style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxFrame, this);
+ return fr;
+}
+
+/*!
+ \fn void Q3GroupBox::setFrameRect(QRect)
+ \internal
+*/
+
+void Q3GroupBox::setFrameRect(QRect r)
+{
+ QRect cr = r.isValid() ? r : rect();
+ if ((d->frameStyle & QFrame::Shape_Mask) == StyledPanel) {
+ cr.adjust(d->leftFrameWidth, d->topFrameWidth, -d->rightFrameWidth, -d->bottomFrameWidth);
+ } else
+ cr.adjust(d->frameWidth, d->frameWidth, -d->frameWidth, -d->frameWidth);
+ setContentsMargins(cr.left(), cr.top(), rect().right() - cr.right(), rect().bottom() - cr.bottom());
+}
+
+/*!
+ \fn int Q3GroupBox::frameWidth() const
+ \internal
+*/
+
+int Q3GroupBox::frameWidth() const
+{
+ return d->frameWidth;
+}
+
+#if defined(Q_MOC_RUN)
+/*!
+ \enum Q3GroupBox::FrameShape
+ \internal
+
+ \value Box
+ \value Sunken
+ \value Plain
+ \value Raised
+ \value MShadow
+ \value NoFrame
+ \value Panel
+ \value StyledPanel
+ \value HLine
+ \value VLine
+ \value GroupBoxPanel
+ \value WinPanel
+ \value ToolBarPanel
+ \value MenuBarPanel
+ \value PopupPanel
+ \value LineEditPanel
+ \value TabWidgetPanel
+ \value MShape
+*/
+#else
+/*!
+ \enum Q3GroupBox::DummyFrame
+ \internal
+
+ \value Box
+ \value Sunken
+ \value Plain
+ \value Raised
+ \value MShadow
+ \value NoFrame
+ \value Panel
+ \value StyledPanel
+ \value HLine
+ \value VLine
+ \value GroupBoxPanel
+ \value WinPanel
+ \value ToolBarPanel
+ \value MenuBarPanel
+ \value PopupPanel
+ \value LineEditPanel
+ \value TabWidgetPanel
+ \value MShape
+*/
+#endif
+
+/*!
+ \typedef Q3GroupBox::FrameShape
+ \internal
+*/
+
+/*!
+ \property Q3GroupBox::margin
+ \brief the width of the margin around the contents of the
+ group box.
+*/
+
+/*!
+ \fn void Q3GroupBox::setMargin(int margin)
+ \since 4.2
+
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ This function uses QWidget::setContentsMargins() to set the margin.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int Q3GroupBox::margin() const
+ \since 4.2
+
+ Returns the width of the margin around the contents of the widget.
+
+ This function uses QWidget::getContentsMargins() to get the margin.
+
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3groupbox.h b/src/qt3support/widgets/q3groupbox.h
new file mode 100644
index 0000000..0a6a44b
--- /dev/null
+++ b/src/qt3support/widgets/q3groupbox.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3GROUPBOX_H
+#define Q3GROUPBOX_H
+
+#include <QtGui/qgroupbox.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3GroupBoxPrivate;
+
+class Q_COMPAT_EXPORT Q3GroupBox : public QGroupBox
+{
+ Q_OBJECT
+public:
+ enum
+#if defined(Q_MOC_RUN)
+ FrameShape
+#else
+ DummyFrame
+#endif
+ { Box = QFrame::Box, Sunken = QFrame::Sunken, Plain = QFrame::Plain,
+ Raised = QFrame::Raised, MShadow=QFrame::Shadow_Mask, NoFrame = QFrame::NoFrame,
+ Panel = QFrame::Panel, StyledPanel = QFrame::StyledPanel, HLine = QFrame::HLine,
+ VLine = QFrame::VLine,
+ WinPanel = QFrame::WinPanel,ToolBarPanel = QFrame::StyledPanel,
+ MenuBarPanel = QFrame::StyledPanel, PopupPanel = QFrame::StyledPanel,
+ LineEditPanel = QFrame::StyledPanel,TabWidgetPanel = QFrame::StyledPanel,
+ GroupBoxPanel = 0x0007,
+ MShape = QFrame::Shape_Mask};
+
+ typedef DummyFrame FrameShape;
+ Q_ENUMS(FrameShape)
+
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation DESIGNABLE false)
+ Q_PROPERTY(int columns READ columns WRITE setColumns DESIGNABLE false)
+
+ Q_PROPERTY(QRect frameRect READ frameRect WRITE setFrameRect DESIGNABLE false)
+ Q_PROPERTY(FrameShape frameShape READ frameShape WRITE setFrameShape)
+ Q_PROPERTY(FrameShape frameShadow READ frameShadow WRITE setFrameShadow)
+ Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth)
+ Q_PROPERTY(int midLineWidth READ midLineWidth WRITE setMidLineWidth)
+ Q_PROPERTY(int margin READ margin WRITE setMargin)
+
+public:
+ explicit Q3GroupBox(QWidget* parent=0, const char* name=0);
+ explicit Q3GroupBox(const QString &title,
+ QWidget* parent=0, const char* name=0);
+ Q3GroupBox(int strips, Qt::Orientation o,
+ QWidget* parent=0, const char* name=0);
+ Q3GroupBox(int strips, Qt::Orientation o, const QString &title,
+ QWidget* parent=0, const char* name=0);
+ ~Q3GroupBox();
+
+ virtual void setColumnLayout(int strips, Qt::Orientation o);
+
+ int columns() const;
+ void setColumns(int);
+
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation);
+
+ int insideMargin() const;
+ int insideSpacing() const;
+ void setInsideMargin(int m);
+ void setInsideSpacing(int s);
+
+ void addSpace(int);
+
+ void setFrameRect(QRect);
+ QRect frameRect() const;
+#ifdef qdoc
+ void setFrameShadow(FrameShape);
+ FrameShape frameShadow() const;
+ void setFrameShape(FrameShape);
+ FrameShape frameShape() const;
+#else
+ void setFrameShadow(DummyFrame);
+ DummyFrame frameShadow() const;
+ void setFrameShape(DummyFrame);
+ DummyFrame frameShape() const;
+#endif
+ void setFrameStyle(int);
+ int frameStyle() const;
+ int frameWidth() const;
+ void setLineWidth(int);
+ int lineWidth() const;
+ void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+ void setMidLineWidth(int);
+ int midLineWidth() const;
+
+protected:
+ void childEvent(QChildEvent *);
+ void resizeEvent(QResizeEvent *);
+ void changeEvent(QEvent *);
+ bool event(QEvent *);
+
+private:
+ void skip();
+ void init();
+ void calculateFrame();
+ void insertWid(QWidget*);
+ void drawFrame(QPainter *p);
+
+ Q3GroupBoxPrivate * d;
+
+ Q_DISABLE_COPY(Q3GroupBox)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3GROUPBOX_H
diff --git a/src/qt3support/widgets/q3hbox.cpp b/src/qt3support/widgets/q3hbox.cpp
new file mode 100644
index 0000000..8c89b63
--- /dev/null
+++ b/src/qt3support/widgets/q3hbox.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3hbox.h"
+#include "qlayout.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3HBox
+ \brief The Q3HBox widget provides horizontal geometry management
+ for its child widgets.
+
+ \compat
+
+ All the horizontal box's child widgets will be placed alongside
+ each other and sized according to their sizeHint()s.
+
+ Use setMargin() to add space around the edges, and use
+ setSpacing() to add space between the widgets. Use
+ setStretchFactor() if you want the widgets to be different sizes
+ in proportion to one another. (See \link layout.html
+ Layouts\endlink for more information on stretch factors.)
+
+ \img qhbox-m.png Q3HBox
+
+ \sa QHBoxLayout Q3VBox Q3Grid
+*/
+
+
+/*!
+ Constructs an hbox widget with parent \a parent, called \a name.
+ The parent, name and widget flags, \a f, are passed to the Q3Frame
+ constructor.
+*/
+Q3HBox::Q3HBox(QWidget *parent, const char *name, Qt::WindowFlags f)
+ :Q3Frame(parent, name, f)
+{
+ (new QHBoxLayout(this, frameWidth(), frameWidth(), name))->setAutoAdd(true);
+}
+
+
+/*!
+ Constructs a horizontal hbox if \a horizontal is TRUE, otherwise
+ constructs a vertical hbox (also known as a vbox).
+
+ This constructor is provided for the QVBox class. You should never
+ need to use it directly.
+
+ The \a parent, \a name and widget flags, \a f, are passed to the
+ Q3Frame constructor.
+*/
+
+Q3HBox::Q3HBox(bool horizontal, QWidget *parent , const char *name, Qt::WindowFlags f)
+ :Q3Frame(parent, name, f)
+{
+ (new QBoxLayout(this, horizontal ? QBoxLayout::LeftToRight : QBoxLayout::Down,
+ frameWidth(), frameWidth(), name))->setAutoAdd(true);
+}
+
+/*!\reimp
+ */
+void Q3HBox::frameChanged()
+{
+ if (layout())
+ layout()->setMargin(frameWidth());
+}
+
+
+/*!
+ Sets the spacing between the child widgets to \a space.
+*/
+
+void Q3HBox::setSpacing(int space)
+{
+ if (layout())
+ layout()->setSpacing(space);
+}
+
+
+/*!
+ \reimp
+*/
+
+QSize Q3HBox::sizeHint() const
+{
+ QWidget *mThis = (QWidget*)this;
+ QApplication::sendPostedEvents(mThis, QEvent::ChildInserted);
+ return Q3Frame::sizeHint();
+}
+
+/*!
+ Sets the stretch factor of widget \a w to \a stretch. Returns true if
+ \a w is found. Otherwise returns false.
+
+ \sa QBoxLayout::setStretchFactor() \link layout.html Layouts\endlink
+*/
+bool Q3HBox::setStretchFactor(QWidget* w, int stretch)
+{
+ QApplication::sendPostedEvents(this, QEvent::ChildInserted);
+ if (QBoxLayout *lay = qobject_cast<QBoxLayout *>(layout()))
+ return lay->setStretchFactor(w, stretch);
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3hbox.h b/src/qt3support/widgets/q3hbox.h
new file mode 100644
index 0000000..abe3900
--- /dev/null
+++ b/src/qt3support/widgets/q3hbox.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3HBOX_H
+#define Q3HBOX_H
+
+#include <Qt3Support/q3frame.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class QBoxLayout;
+
+class Q_COMPAT_EXPORT Q3HBox : public Q3Frame
+{
+ Q_OBJECT
+public:
+ Q3HBox(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0);
+
+ void setSpacing(int);
+ bool setStretchFactor(QWidget*, int stretch);
+ QSize sizeHint() const;
+
+protected:
+ Q3HBox(bool horizontal, QWidget* parent, const char* name, Qt::WindowFlags f = 0);
+ void frameChanged();
+
+private:
+ Q_DISABLE_COPY(Q3HBox)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3HBOX_H
diff --git a/src/qt3support/widgets/q3header.cpp b/src/qt3support/widgets/q3header.cpp
new file mode 100644
index 0000000..bbe156a
--- /dev/null
+++ b/src/qt3support/widgets/q3header.cpp
@@ -0,0 +1,2040 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3header.h"
+#ifndef QT_NO_HEADER
+#include "qapplication.h"
+#include "qbitarray.h"
+#include "qcursor.h"
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qvector.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3HeaderData
+{
+public:
+ Q3HeaderData(int n)
+ {
+ count = n;
+ sizes.resize(n);
+ positions.resize(n);
+ labels.resize(n);
+ nullStringLabels.resize(n);
+ icons.resize(n);
+ i2s.resize(n);
+ s2i.resize(n);
+ clicks.resize(n);
+ resize.resize(n);
+ int p =0;
+ for (int i = 0; i < n; i ++) {
+ sizes[i] = 88;
+ i2s[i] = i;
+ s2i[i] = i;
+ positions[i] = p;
+ p += sizes[i];
+ }
+ clicks_default = true;
+ resize_default = true;
+ clicks.fill(clicks_default);
+ resize.fill(resize_default);
+ move = true;
+ sortSection = -1;
+ sortDirection = true;
+ positionsDirty = true;
+ lastPos = 0;
+ fullSize = -2;
+ pos_dirty = false;
+ is_a_table_header = false;
+ focusIdx = 0;
+ }
+ ~Q3HeaderData()
+ {
+ for (int i = 0; i < icons.size(); ++i)
+ delete icons.at(i);
+ }
+
+
+ QVector<int> sizes;
+ int height; // we abuse the heights as widths for vertical layout
+ bool heightDirty;
+ QVector<int> positions; // sorted by index
+ QVector<QString> labels;
+ QVector<QIcon *> icons;
+ QVector<int> i2s;
+ QVector<int> s2i;
+
+ QBitArray clicks;
+ QBitArray resize;
+ QBitArray nullStringLabels;
+ uint move : 1;
+ uint clicks_default : 1; // default value for new clicks bits
+ uint resize_default : 1; // default value for new resize bits
+ uint pos_dirty : 1;
+ uint is_a_table_header : 1;
+ bool sortDirection;
+ bool positionsDirty;
+ int sortSection;
+ int count;
+ int lastPos;
+ int fullSize;
+ int focusIdx;
+ int pressDelta;
+
+ int sectionAt(int pos) {
+ // positions is sorted by index, not by section
+ if (!count)
+ return -1;
+ int l = 0;
+ int r = count - 1;
+ int i = ((l+r+1) / 2);
+ while (r - l) {
+ if (positions[i] > pos)
+ r = i -1;
+ else
+ l = i;
+ i = ((l+r+1) / 2);
+ }
+ if (positions[i] <= pos && pos <= positions[i] + sizes[i2s[i]])
+ return i2s[i];
+ return -1;
+ }
+};
+
+static QStyleOptionHeader getStyleOption(const Q3Header *header, int section)
+{
+ QStyleOptionHeader opt;
+ opt.init(header);
+ opt.section = section;
+ opt.textAlignment = Qt::AlignVCenter;
+ opt.iconAlignment = Qt::AlignVCenter;
+ if (header->iconSet(section))
+ opt.icon = *header->iconSet(section);
+ opt.text = header->label(section);
+ if (header->orientation() == Qt::Horizontal)
+ opt.state = QStyle::State_Horizontal;
+ return opt;
+}
+
+bool qt_get_null_label_bit(Q3HeaderData *data, int section)
+{
+ return data->nullStringLabels.testBit(section);
+}
+
+void qt_set_null_label_bit(Q3HeaderData *data, int section, bool b)
+{
+ data->nullStringLabels.setBit(section, b);
+}
+
+/*!
+ \class Q3Header
+ \brief The Q3Header class provides a header row or column, e.g. for
+ tables and listviews.
+
+ \compat
+
+ This class provides a header, e.g. a vertical header to display
+ row labels, or a horizontal header to display column labels. It is
+ used by Q3Table and Q3ListView for example.
+
+ A header is composed of one or more \e sections, each of which can
+ display a text label and an \link QIcon icon\endlink. A sort
+ indicator (an arrow) can also be displayed using
+ setSortIndicator().
+
+ Sections are added with addLabel() and removed with removeLabel().
+ The label and icon are set in addLabel() and can be changed
+ later with setLabel(). Use count() to retrieve the number of
+ sections in the header.
+
+ The orientation of the header is set with setOrientation(). If
+ setStretchEnabled() is true, the sections will expand to take up
+ the full width (height for vertical headers) of the header. The
+ user can resize the sections manually if setResizeEnabled() is
+ true. Call adjustHeaderSize() to have the sections resize to
+ occupy the full width (or height).
+
+ A section can be moved with moveSection(). If setMovingEnabled()
+ is true (the default)the user may drag a section from one position
+ to another. If a section is moved, the index positions at which
+ sections were added (with addLabel()), may not be the same after the
+ move. You don't have to worry about this in practice because the
+ Q3Header API works in terms of section numbers, so it doesn't matter
+ where a particular section has been moved to.
+
+ If you want the current index position of a section call
+ mapToIndex() giving it the section number. (This is the number
+ returned by the addLabel() call which created the section.) If you
+ want to get the section number of a section at a particular index
+ position call mapToSection() giving it the index number.
+
+ Here's an example to clarify mapToSection() and mapToIndex():
+
+ \table
+ \header \i41 Index positions
+ \row \i 0 \i 1 \i 2 \i 3
+ \header \i41 Original section ordering
+ \row \i Sect 0 \i Sect 1 \i Sect 2 \i Sect 3
+ \header \i41 Ordering after the user moves a section
+ \row \i Sect 0 \i Sect 2 \i Sect 3 \i Sect 1
+ \endtable
+
+ \table
+ \header \i \e k \i mapToSection(\e k) \i mapToIndex(\e k)
+ \row \i 0 \i 0 \i 0
+ \row \i 1 \i 2 \i 3
+ \row \i 2 \i 3 \i 1
+ \row \i 3 \i 1 \i 2
+ \endtable
+
+ In the example above, if we wanted to find out which section is at
+ index position 3 we'd call mapToSection(3) and get a section
+ number of 1 since section 1 was moved. Similarly, if we wanted to
+ know which index position section 2 occupied we'd call
+ mapToIndex(2) and get an index of 1.
+
+ Q3Header provides the clicked(), pressed() and released() signals.
+ If the user changes the size of a section, the sizeChange() signal
+ is emitted. If you want to have a sizeChange() signal emitted
+ continuously whilst the user is resizing (rather than just after
+ the resizing is finished), use setTracking(). If the user moves a
+ section the indexChange() signal is emitted.
+
+ \sa Q3ListView Q3Table
+*/
+
+
+
+/*!
+ Constructs a horizontal header called \a name, with parent \a
+ parent.
+*/
+
+Q3Header::Q3Header(QWidget *parent, const char *name)
+ : QWidget(parent, name, Qt::WStaticContents)
+{
+ orient = Qt::Horizontal;
+ init(0);
+}
+
+/*!
+ Constructs a horizontal header called \a name, with \a n sections
+ and parent \a parent.
+*/
+
+Q3Header::Q3Header(int n, QWidget *parent, const char *name)
+ : QWidget(parent, name, Qt::WStaticContents)
+{
+ orient = Qt::Horizontal;
+ init(n);
+}
+
+/*!
+ Destroys the header and all its sections.
+*/
+
+Q3Header::~Q3Header()
+{
+ delete d;
+ d = 0;
+}
+
+/*! \reimp
+ */
+
+void Q3Header::showEvent(QShowEvent *e)
+{
+ calculatePositions();
+ QWidget::showEvent(e);
+}
+
+/*!
+ \fn void Q3Header::sizeChange(int section, int oldSize, int newSize)
+
+ This signal is emitted when the user has changed the size of a \a
+ section from \a oldSize to \a newSize. This signal is typically
+ connected to a slot that repaints the table or list that contains
+ the header.
+*/
+
+/*!
+ \fn void Q3Header::clicked(int section)
+
+ If isClickEnabled() is true, this signal is emitted when the user
+ clicks section \a section.
+
+ \sa pressed(), released()
+*/
+
+/*!
+ \fn void Q3Header::pressed(int section)
+
+ This signal is emitted when the user presses section \a section
+ down.
+
+ \sa released()
+*/
+
+/*!
+ \fn void Q3Header::released(int section)
+
+ This signal is emitted when section \a section is released.
+
+ \sa pressed()
+*/
+
+
+/*!
+ \fn void Q3Header::indexChange(int section, int fromIndex, int toIndex)
+
+ This signal is emitted when the user moves section \a section from
+ index position \a fromIndex, to index position \a toIndex.
+*/
+
+/*!
+ \fn void Q3Header::moved(int fromIndex, int toIndex)
+
+ Use indexChange() instead.
+
+ This signal is emitted when the user has moved the section which
+ is displayed at the index \a fromIndex to the index \a toIndex.
+*/
+
+/*!
+ \fn void Q3Header::sectionClicked(int index)
+
+ Use clicked() instead.
+
+ This signal is emitted when a part of the header is clicked. \a
+ index is the index at which the section is displayed.
+
+ In a list view this signal would typically be connected to a slot
+ that sorts the specified column (or row).
+*/
+
+/*! \fn int Q3Header::cellSize(int) const
+
+ Use sectionSize() instead.
+
+ Returns the size in pixels of the section that is displayed at
+ the index \a i.
+*/
+
+/*!
+ \fn void Q3Header::sectionHandleDoubleClicked(int section)
+
+ This signal is emitted when the user doubleclicks on the edge
+ (handle) of section \a section.
+*/
+
+/*!
+
+ Use sectionPos() instead.
+
+ Returns the position in pixels of the section that is displayed at the
+ index \a i. The position is measured from the start of the header.
+*/
+
+int Q3Header::cellPos(int i) const
+{
+ if (i == count() && i > 0)
+ return d->positions[i-1] + d->sizes[d->i2s[i-1]]; // compatibility
+ return sectionPos(mapToSection(i));
+}
+
+
+/*!
+ \property Q3Header::count
+ \brief the number of sections in the header
+*/
+
+int Q3Header::count() const
+{
+ return d->count;
+}
+
+
+/*!
+ \property Q3Header::tracking
+ \brief whether the sizeChange() signal is emitted continuously
+
+ If tracking is on, the sizeChange() signal is emitted continuously
+ while the mouse is moved (i.e. when the header is resized),
+ otherwise it is only emitted when the mouse button is released at
+ the end of resizing.
+
+ Tracking defaults to false.
+*/
+
+
+/*
+ Initializes with \a n columns.
+*/
+void Q3Header::init(int n)
+{
+ state = Idle;
+ cachedPos = 0; // unused
+ d = new Q3HeaderData(n);
+ d->height = 0;
+ d->heightDirty = true;
+ offs = 0;
+ if(reverse())
+ offs = d->lastPos - width();
+ oldHandleIdx = oldHIdxSize = handleIdx = 0;
+
+ setMouseTracking(true);
+ trackingIsOn = false;
+ setBackgroundRole(QPalette::Button);
+ setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ setAttribute(Qt::WA_PaintOutsidePaintEvent);
+}
+
+/*!
+ \property Q3Header::orientation
+ \brief the header's orientation
+
+ The orientation is either Qt::Vertical or Qt::Horizontal (the
+ default).
+
+ Call setOrientation() before adding labels if you don't provide a
+ size parameter otherwise the sizes will be incorrect.
+*/
+
+void Q3Header::setOrientation(Qt::Orientation orientation)
+{
+ if (orient == orientation)
+ return;
+ orient = orientation;
+ if (orient == Qt::Horizontal)
+ setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ else
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
+ update();
+ updateGeometry();
+}
+
+
+/*
+ Paints a rectangle starting at \a p, with length \s.
+*/
+void Q3Header::paintRect(int p, int s)
+{
+ QPainter paint(this);
+ paint.setPen(QPen(Qt::black, 1, Qt::DotLine));
+ if (reverse())
+ paint.drawRect(p - s, 3, s, height() - 5);
+ else if (orient == Qt::Horizontal)
+ paint.drawRect(p, 3, s, height() - 5);
+ else
+ paint.drawRect(3, p, height() - 5, s);
+}
+
+/*
+ Marks the division line at \a idx.
+*/
+void Q3Header::markLine(int idx)
+{
+ QPainter paint(this);
+ paint.setPen(QPen(Qt::black, 1, Qt::DotLine));
+ int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
+ int p = pPos(idx);
+ int x = p - MARKSIZE/2;
+ int y = 2;
+ int x2 = p + MARKSIZE/2;
+ int y2 = height() - 3;
+ if (orient == Qt::Vertical) {
+ int t = x; x = y; y = t;
+ t = x2; x2 = y2; y2 = t;
+ }
+
+ paint.drawLine(x, y, x2, y);
+ paint.drawLine(x, y+1, x2, y+1);
+
+ paint.drawLine(x, y2, x2, y2);
+ paint.drawLine(x, y2-1, x2, y2-1);
+
+ paint.drawLine(x, y, x, y2);
+ paint.drawLine(x+1, y, x+1, y2);
+
+ paint.drawLine(x2, y, x2, y2);
+ paint.drawLine(x2-1, y, x2-1, y2);
+}
+
+/*
+ Removes the mark at the division line at \a idx.
+*/
+void Q3Header::unMarkLine(int idx)
+{
+ if (idx < 0)
+ return;
+ int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
+ int p = pPos(idx);
+ int x = p - MARKSIZE/2;
+ int y = 2;
+ int x2 = p + MARKSIZE/2;
+ int y2 = height() - 3;
+ if (orient == Qt::Vertical) {
+ int t = x; x = y; y = t;
+ t = x2; x2 = y2; y2 = t;
+ }
+ repaint(x, y, x2-x+1, y2-y+1);
+}
+
+/*! \fn int Q3Header::cellAt(int) const
+
+ Use sectionAt() instead.
+
+ Returns the index at which the section is displayed, which contains
+ \a pos in widget coordinates, or -1 if \a pos is outside the header
+ sections.
+*/
+
+/*
+ Tries to find a line that is not a neighbor of \c handleIdx.
+*/
+int Q3Header::findLine(int c)
+{
+ int i = 0;
+ if (c > d->lastPos || (reverse() && c < 0)) {
+ return d->count;
+ } else {
+ int section = sectionAt(c);
+ if (section < 0)
+ return handleIdx;
+ i = d->s2i[section];
+ }
+ int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
+ if (i == handleIdx)
+ return i;
+ if (i == handleIdx - 1 && pPos(handleIdx) - c > MARKSIZE/2)
+ return i;
+ if (i == handleIdx + 1 && c - pPos(i) > MARKSIZE/2)
+ return i + 1;
+ if (c - pPos(i) > pSize(i) / 2)
+ return i + 1;
+ else
+ return i;
+}
+
+/*!
+ Returns the handle at position \a p, or -1 if there is no handle at \a p.
+*/
+int Q3Header::handleAt(int p)
+{
+ int section = d->sectionAt(p);
+ if (section >= 0) {
+ int GripMargin = (bool)d->resize[section] ?
+ style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
+ int index = d->s2i[section];
+ if ((index > 0 && p < d->positions[index] + GripMargin) ||
+ (p > d->positions[index] + d->sizes[section] - GripMargin)) {
+ if (index > 0 && p < d->positions[index] + GripMargin)
+ section = d->i2s[--index];
+ // don't show icon if streaching is enabled it is at the end of the last section
+ if (d->resize.testBit(section) && (d->fullSize == -2 || index != count() - 1)) {
+ return section;
+ }
+ }
+ }
+
+ return -1;
+}
+
+/*!
+ Use moveSection() instead.
+
+ Moves the section that is currently displayed at index \a fromIdx
+ to index \a toIdx.
+*/
+
+void Q3Header::moveCell(int fromIdx, int toIdx)
+{
+ moveSection(mapToSection(fromIdx), toIdx);
+}
+
+
+
+/*!
+ Move and signal and repaint.
+ */
+
+void Q3Header::handleColumnMove(int fromIdx, int toIdx)
+{
+ int s = d->i2s[fromIdx];
+ if (fromIdx < toIdx)
+ toIdx++; //Convert to
+ QRect r = sRect(fromIdx);
+ r |= sRect(toIdx);
+ moveSection(s, toIdx);
+ update(r);
+ emit moved(fromIdx, toIdx);
+ emit indexChange(s, fromIdx, toIdx);
+}
+
+/*!
+ \reimp
+*/
+void Q3Header::keyPressEvent(QKeyEvent *e)
+{
+ int i = d->focusIdx;
+ if (e->key() == Qt::Key_Space) {
+ //don't do it if we're doing something with the mouse
+ if (state == Idle && d->clicks[d->i2s[d->focusIdx] ]) {
+ handleIdx = i;
+ state = Pressed;
+ repaint(sRect(handleIdx));
+ emit pressed(d->i2s[i]);
+ }
+ } else if ((orientation() == Qt::Horizontal && (e->key() == Qt::Key_Right || e->key() == Qt::Key_Left))
+ || (orientation() == Qt::Vertical && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down))) {
+ int dir = e->key() == Qt::Key_Right || e->key() == Qt::Key_Down ? 1 : -1;
+ int s = d->i2s[i];
+ if (e->state() & Qt::ControlButton && d->resize[s]) {
+ //resize
+ int step = e->state() & Qt::ShiftButton ? dir : 10*dir;
+ int c = d->positions[i] + d->sizes[s] + step;
+ handleColumnResize(i, c, true);
+ } else if (e->state() & (Qt::AltButton|Qt::MetaButton) && d->move) {
+ //move section
+ int i2 = (i + count() + dir) % count();
+ d->focusIdx = i2;
+ handleColumnMove(i, i2);
+ } else {
+ //focus on different section
+ QRect r = sRect(d->focusIdx);
+ d->focusIdx = (d->focusIdx + count() + dir) % count();
+ r |= sRect(d->focusIdx);
+ update(r);
+ }
+ } else {
+ e->ignore();
+ }
+}
+
+/*!
+ \reimp
+*/
+void Q3Header::keyReleaseEvent(QKeyEvent *e)
+{
+ switch (e->key()) {
+ case Qt::Key_Space:
+ //double check that this wasn't started with the mouse
+ if (state == Pressed && handleIdx == d->focusIdx) {
+ repaint(sRect(handleIdx));
+ int section = d->i2s[d->focusIdx];
+ emit released(section);
+ emit sectionClicked(handleIdx);
+ emit clicked(section);
+ state = Idle;
+ handleIdx = -1;
+ }
+ break;
+ default:
+ e->ignore();
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void Q3Header::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() != Qt::LeftButton || state != Idle)
+ return;
+ oldHIdxSize = handleIdx;
+ handleIdx = 0;
+ int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
+ c += offset();
+ if (reverse())
+ c = d->lastPos - c;
+
+ int section = d->sectionAt(c);
+ if (section < 0)
+ return;
+ int GripMargin = (bool)d->resize[section] ?
+ style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
+ int index = d->s2i[section];
+
+ if ((index > 0 && c < d->positions[index] + GripMargin) ||
+ (c > d->positions[index] + d->sizes[section] - GripMargin)) {
+ if (c < d->positions[index] + GripMargin)
+ handleIdx = index-1;
+ else
+ handleIdx = index;
+ if (d->lastPos <= (orient == Qt::Horizontal ? width() :
+ height()) && d->fullSize != -2 && handleIdx == count() - 1) {
+ handleIdx = -1;
+ return;
+ }
+ oldHIdxSize = d->sizes[d->i2s[handleIdx]];
+ state = d->resize[d->i2s[handleIdx] ] ? Sliding : Blocked;
+ } else if (index >= 0) {
+ oldHandleIdx = handleIdx = index;
+ moveToIdx = -1;
+ state = d->clicks[d->i2s[handleIdx] ] ? Pressed : Blocked;
+ clickPos = c;
+ repaint(sRect(handleIdx));
+ if(oldHandleIdx != handleIdx)
+ repaint(sRect(oldHandleIdx));
+ emit pressed(section);
+ }
+
+ d->pressDelta = c - (d->positions[handleIdx] + d->sizes[d->i2s[handleIdx]]);
+}
+
+/*!
+ \reimp
+*/
+void Q3Header::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() != Qt::LeftButton)
+ return;
+ int oldOldHandleIdx = oldHandleIdx;
+ State oldState = state;
+ state = Idle;
+ switch (oldState) {
+ case Pressed: {
+ int section = d->i2s[handleIdx];
+ emit released(section);
+ if (sRect(handleIdx).contains(e->pos())) {
+ oldHandleIdx = handleIdx;
+ emit sectionClicked(handleIdx);
+ emit clicked(section);
+ } else {
+ handleIdx = oldHandleIdx;
+ }
+ repaint(sRect(handleIdx));
+ if (oldOldHandleIdx != handleIdx)
+ repaint(sRect(oldOldHandleIdx));
+ } break;
+ case Sliding: {
+ int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
+ c += offset();
+ if (reverse())
+ c = d->lastPos - c;
+ handleColumnResize(handleIdx, c - d->pressDelta, true);
+ } break;
+ case Moving: {
+#ifndef QT_NO_CURSOR
+ unsetCursor();
+#endif
+ int section = d->i2s[handleIdx];
+ if (handleIdx != moveToIdx && moveToIdx != -1) {
+ moveSection(section, moveToIdx);
+ handleIdx = oldHandleIdx;
+ emit moved(handleIdx, moveToIdx);
+ emit indexChange(section, handleIdx, moveToIdx);
+ emit released(section);
+ repaint(); // a bit overkill, but removes the handle as well
+ } else {
+ if (sRect(handleIdx).contains(e->pos())) {
+ oldHandleIdx = handleIdx;
+ emit released(section);
+ emit sectionClicked(handleIdx);
+ emit clicked(section);
+ } else {
+ handleIdx = oldHandleIdx;
+ }
+ repaint(sRect(handleIdx));
+ if(oldOldHandleIdx != handleIdx)
+ repaint(sRect(oldOldHandleIdx));
+ }
+ break;
+ }
+ case Blocked:
+ //nothing
+ break;
+ default:
+ // empty, probably. Idle, at any rate.
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+void Q3Header::mouseMoveEvent(QMouseEvent *e)
+{
+ int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
+ c += offset();
+
+ int pos = c;
+ if(reverse())
+ c = d->lastPos - c;
+
+ switch(state) {
+ case Idle:
+#ifndef QT_NO_CURSOR
+ if (handleAt(c) < 0)
+ unsetCursor();
+ else if (orient == Qt::Horizontal)
+ setCursor(Qt::splitHCursor);
+ else
+ setCursor(Qt::splitVCursor);
+#endif
+ break;
+ case Blocked:
+ break;
+ case Pressed:
+ if (QABS(c - clickPos) > 4 && d->move) {
+ state = Moving;
+ moveToIdx = -1;
+#ifndef QT_NO_CURSOR
+ if (orient == Qt::Horizontal)
+ setCursor(Qt::SizeHorCursor);
+ else
+ setCursor(Qt::SizeVerCursor);
+#endif
+ }
+ break;
+ case Sliding:
+ handleColumnResize(handleIdx, c, false, false);
+ break;
+ case Moving: {
+ int newPos = findLine(pos);
+ if (newPos != moveToIdx) {
+ if (moveToIdx == handleIdx || moveToIdx == handleIdx + 1)
+ repaint(sRect(handleIdx));
+ else
+ unMarkLine(moveToIdx);
+ moveToIdx = newPos;
+ if (moveToIdx == handleIdx || moveToIdx == handleIdx + 1)
+ paintRect(pPos(handleIdx), pSize(handleIdx));
+ else
+ markLine(moveToIdx);
+ }
+ break;
+ }
+ default:
+ qWarning("Q3Header::mouseMoveEvent: (%s) unknown state", objectName().toLocal8Bit().data());
+ break;
+ }
+}
+
+/*! \reimp */
+
+void Q3Header::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ int p = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
+ p += offset();
+ if(reverse())
+ p = d->lastPos - p;
+
+ int header = handleAt(p);
+ if (header >= 0)
+ emit sectionHandleDoubleClicked(header);
+}
+
+/*
+ Handles resizing of sections. This means it redraws the relevant parts
+ of the header.
+*/
+
+void Q3Header::handleColumnResize(int index, int c, bool final, bool recalcAll)
+{
+ int section = d->i2s[index];
+ int GripMargin = (bool)d->resize[section] ?
+ style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
+ int lim = d->positions[index] + 2*GripMargin;
+ if (c == lim)
+ return;
+ if (c < lim)
+ c = lim;
+ int oldSize = d->sizes[section];
+ int newSize = c - d->positions[index];
+ d->sizes[section] = newSize;
+
+ calculatePositions(!recalcAll, !recalcAll ? section : 0);
+
+ int pos = d->positions[index]-offset();
+ if(reverse()) // repaint the whole thing. Could be optimized (lars)
+ repaint(0, 0, width(), height());
+ else if (orient == Qt::Horizontal)
+ repaint(pos, 0, width() - pos, height());
+ else
+ repaint(0, pos, width(), height() - pos);
+
+ int os = 0, ns = 0;
+ if (tracking() && oldSize != newSize) {
+ os = oldSize;
+ ns = newSize;
+ emit sizeChange(section, oldSize, newSize);
+ } else if (!tracking() && final && oldHIdxSize != newSize) {
+ os = oldHIdxSize;
+ ns = newSize;
+ emit sizeChange(section, oldHIdxSize, newSize);
+ }
+
+ if (os != ns) {
+ if (d->fullSize == -1) {
+ d->fullSize = count() - 1;
+ adjustHeaderSize();
+ d->fullSize = -1;
+ } else if (d->fullSize >= 0) {
+ int old = d->fullSize;
+ d->fullSize = count() - 1;
+ adjustHeaderSize();
+ d->fullSize = old;
+ }
+ }
+}
+
+/*!
+ Returns the rectangle covered by the section at index \a index.
+*/
+
+QRect Q3Header::sRect(int index)
+{
+
+ int section = mapToSection(index);
+ if (count() > 0 && index >= count()) {
+ int s = d->positions[count() - 1] - offset() +
+ d->sizes[mapToSection(count() - 1)];
+ if (orient == Qt::Horizontal)
+ return QRect(s, 0, width() - s + 10, height());
+ else
+ return QRect(0, s, width(), height() - s + 10);
+ }
+ if (section < 0)
+ return rect(); // ### eeeeevil
+
+ if (reverse())
+ return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(),
+ 0, d->sizes[section], height());
+ else if (orient == Qt::Horizontal)
+ return QRect( d->positions[index]-offset(), 0, d->sizes[section], height());
+ else
+ return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]);
+}
+
+/*!
+ Returns the rectangle covered by section \a section.
+*/
+
+QRect Q3Header::sectionRect(int section) const
+{
+ int index = mapToIndex(section);
+ if (section < 0)
+ return rect(); // ### eeeeevil
+
+ if (reverse())
+ return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(),
+ 0, d->sizes[section], height());
+ else if (orient == Qt::Horizontal)
+ return QRect( d->positions[index]-offset(), 0, d->sizes[section], height());
+ else
+ return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]);
+}
+
+/*!
+ \overload
+
+ Sets the icon for section \a section to \a icon and the text to
+ \a s. The section's width is set to \a size if \a size \>= 0;
+ otherwise it is left unchanged.
+
+ If the section does not exist, nothing happens.
+*/
+
+void Q3Header::setLabel(int section, const QIcon& icon,
+ const QString &s, int size)
+{
+ if (section < 0 || section >= count())
+ return;
+ delete d->icons[section];
+ d->icons[section] = new QIcon(icon);
+ setLabel(section, s, size);
+}
+
+/*!
+ Sets the text of section \a section to \a s. The section's width
+ is set to \a size if \a size \>= 0; otherwise it is left
+ unchanged. Any icon set that has been set for this section remains
+ unchanged.
+
+ If the section does not exist, nothing happens.
+*/
+void Q3Header::setLabel(int section, const QString &s, int size)
+{
+ if (section < 0 || section >= count())
+ return;
+ d->labels[section] = s;
+ d->nullStringLabels.setBit(section, s.isNull());
+
+ setSectionSizeAndHeight(section, size);
+
+ if (updatesEnabled()) {
+ updateGeometry();
+ calculatePositions();
+ update();
+ }
+}
+
+
+bool qt_qheader_label_return_null_strings = false;
+/*!
+ Returns the text for section \a section. If the section does not
+ exist, returns an empty string.
+*/
+QString Q3Header::label(int section) const
+{
+ if (section < 0 || section >= count())
+ return QString();
+ QString l = d->labels.value(section);
+ if (!l.isNull())
+ return l;
+ if (d->nullStringLabels.testBit(section) || qt_qheader_label_return_null_strings)
+ return l;
+ else
+ return QString::number(section + 1);
+}
+
+/*!
+ Returns the icon set for section \a section. If the section does
+ not exist, 0 is returned.
+*/
+
+QIcon *Q3Header::iconSet(int section) const
+{
+ if (section < 0 || section >= count())
+ return 0;
+ return d->icons[section];
+}
+
+
+/*!
+ \overload
+
+ Adds a new section with icon \a icon and label text \a s.
+ Returns the index position where the section was added (at the
+ right for horizontal headers, at the bottom for vertical headers).
+ The section's width is set to \a size, unless size is negative in
+ which case the size is calculated taking account of the size of
+ the text.
+*/
+int Q3Header::addLabel(const QIcon& icon, const QString &s, int size)
+{
+ int n = count() + 1;
+ d->icons.resize(n + 1);
+ d->icons.insert(n - 1, new QIcon(icon));
+ return addLabel(s, size);
+}
+
+/*!
+ Removes section \a section. If the section does not exist, nothing
+ happens.
+*/
+void Q3Header::removeLabel(int section)
+{
+ if (section < 0 || section > count() - 1)
+ return;
+
+ int index = d->s2i[section];
+ int n = --d->count;
+ int i;
+ for (i = section; i < n; ++i) {
+ d->sizes[i] = d->sizes[i+1];
+ d->labels[i] = d->labels[i+1];
+ d->labels[i+1] = QString();
+ d->nullStringLabels[i] = d->nullStringLabels[i+1];
+ d->nullStringLabels[i+1] = 0;
+ d->icons[i] = d->icons[i+1];
+ d->icons[i+1] = 0;
+ }
+
+ d->sizes.resize(n);
+ d->positions.resize(n);
+ d->labels.resize(n);
+ d->nullStringLabels.resize(n);
+ d->icons.resize(n);
+
+ for (i = section; i < n; ++i)
+ d->s2i[i] = d->s2i[i+1];
+ d->s2i.resize(n);
+
+ if (updatesEnabled()) {
+ for (i = 0; i < n; ++i)
+ if (d->s2i[i] > index)
+ --d->s2i[i];
+ }
+
+ for (i = index; i < n; ++i)
+ d->i2s[i] = d->i2s[i+1];
+ d->i2s.resize(n);
+
+ if (updatesEnabled()) {
+ for (i = 0; i < n; ++i)
+ if (d->i2s[i] > section)
+ --d->i2s[i];
+ }
+
+ if (updatesEnabled()) {
+ updateGeometry();
+ calculatePositions();
+ update();
+ }
+}
+
+QSize Q3Header::sectionSizeHint(int section, const QFontMetrics& fm) const
+{
+ int iw = 0;
+ int ih = 0;
+ if (d->icons[section] != 0) {
+ QSize isize = d->icons[section]->pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize),
+ QIcon::Normal).size();
+ iw = isize.width() + 2;
+ ih = isize.height();
+ }
+
+ QRect bound;
+ QString label = d->labels[section];
+ if (!label.isNull() || d->nullStringLabels.testBit(section)) {
+ int lines = label.count(QLatin1Char('\n')) + 1;
+ int w = 0;
+ if (lines > 1) {
+ bound.setHeight(fm.height() + fm.lineSpacing() * (lines - 1));
+ QStringList list = label.split(QLatin1Char('\n'));
+ for (int i=0; i < list.count(); ++i) {
+ int tmpw = fm.width(list.at(i));
+ w = QMAX(w, tmpw);
+ }
+ } else {
+ bound.setHeight(fm.height());
+ w = fm.width(label);
+ }
+ bound.setWidth(w);
+ }
+ int arrowWidth = 0;
+ if (d->sortSection == section)
+ arrowWidth = ((orient == Qt::Horizontal ? height() : width()) / 2) + 8;
+ int height = qMax(bound.height() + 2, ih) + 4;
+ int width = bound.width() + style()->pixelMetric(QStyle::PM_HeaderMargin) * 4
+ + iw + arrowWidth;
+ return QSize(width, height);
+}
+
+/*
+ Sets d->sizes[\a section] to a bounding rect based on its size
+ hint and font metrics, but constrained by \a size. It also updates
+ d->height.
+*/
+void Q3Header::setSectionSizeAndHeight(int section, int size)
+{
+ QSize sz = sectionSizeHint(section, fontMetrics());
+
+ if (size < 0) {
+ if (d->sizes[section] < 0)
+ d->sizes[section] = (orient == Qt::Horizontal) ? sz.width()
+ : sz.height();
+ } else {
+ d->sizes[section] = size;
+ }
+
+ int newHeight = (orient == Qt::Horizontal) ? sz.height() : sz.width();
+ if (newHeight > d->height) {
+ d->height = newHeight;
+ } else if (newHeight < d->height) {
+ /*
+ We could be smarter, but we aren't. This makes a difference
+ only for users with many columns and '\n's in their headers
+ at the same time.
+ */
+ d->heightDirty = true;
+ }
+}
+
+/*!
+ Adds a new section with label text \a s. Returns the index
+ position where the section was added (at the right for horizontal
+ headers, at the bottom for vertical headers). The section's width
+ is set to \a size. If \a size \< 0, an appropriate size for the
+ text \a s is chosen.
+*/
+int Q3Header::addLabel(const QString &s, int size)
+{
+ int n = ++d->count;
+ if ((int)d->icons.size() < n )
+ d->icons.resize(n);
+ if ((int)d->sizes.size() < n ) {
+ d->labels.resize(n);
+ d->nullStringLabels.resize(n);
+ d->sizes.resize(n);
+ d->positions.resize(n);
+ d->i2s.resize(n);
+ d->s2i.resize(n);
+ d->clicks.resize(n);
+ d->resize.resize(n);
+ }
+ int section = d->count - 1;
+ if (!d->is_a_table_header || !s.isNull()) {
+ d->labels.insert(section, s);
+ d->nullStringLabels.setBit(section, s.isNull());
+ }
+
+ if (size >= 0 && s.isNull() && d->is_a_table_header) {
+ d->sizes[section] = size;
+ } else {
+ d->sizes[section] = -1;
+ setSectionSizeAndHeight(section, size);
+ }
+
+ int index = section;
+ d->positions[index] = d->lastPos;
+
+ d->s2i[section] = index;
+ d->i2s[index] = section;
+ d->clicks.setBit(section, d->clicks_default);
+ d->resize.setBit(section, d->resize_default);
+
+ if (updatesEnabled()) {
+ updateGeometry();
+ calculatePositions();
+ update();
+ }
+ return index;
+}
+
+void Q3Header::resizeArrays(int size)
+{
+ d->icons.resize(size);
+ d->labels.resize(size);
+ d->nullStringLabels.resize(size);
+ d->sizes.resize(size);
+ d->positions.resize(size);
+ d->i2s.resize(size);
+ d->s2i.resize(size);
+ d->clicks.resize(size);
+ d->resize.resize(size);
+}
+
+void Q3Header::setIsATableHeader(bool b)
+{
+ d->is_a_table_header = b;
+}
+
+/*! \reimp */
+QSize Q3Header::sizeHint() const
+{
+ int width;
+ int height;
+
+ ensurePolished();
+ QFontMetrics fm = fontMetrics();
+
+ if (d->heightDirty) {
+ d->height = fm.lineSpacing() + 6;
+ for (int i = 0; i < count(); i++) {
+ int h = orient == Qt::Horizontal ?
+ sectionSizeHint(i, fm).height() : sectionSizeHint(i, fm).width();
+ d->height = qMax(d->height, h);
+ }
+ d->heightDirty = false;
+ }
+
+ if (orient == Qt::Horizontal) {
+ height = fm.lineSpacing() + 6;
+ width = 0;
+ height = qMax(height, d->height);
+ for (int i = 0; i < count(); i++)
+ width += d->sizes[i];
+ } else {
+ width = fm.width(QLatin1Char(' '));
+ height = 0;
+ width = qMax(width, d->height);
+ for (int i = 0; i < count(); i++)
+ height += d->sizes[i];
+ }
+ QStyleOptionHeader opt = getStyleOption(this, 0);
+ return style()->sizeFromContents(QStyle::CT_Q3Header, &opt, QSize(width, height),
+ this).expandedTo(QApplication::globalStrut());
+}
+
+/*!
+ \property Q3Header::offset
+ \brief the header's left-most (or top-most) visible pixel
+
+ Setting this property will scroll the header so that \e offset
+ becomes the left-most (or top-most for vertical headers) visible
+ pixel.
+*/
+int Q3Header::offset() const
+{
+ if (reverse())
+ return d->lastPos - width() - offs;
+ return offs;
+}
+
+void Q3Header::setOffset(int x)
+{
+ int oldOff = offset();
+ offs = x;
+ if(d->lastPos < (orient == Qt::Horizontal ? width() : height()))
+ offs = 0;
+ else if (reverse())
+ offs = d->lastPos - width() - x;
+ if (orient == Qt::Horizontal)
+ scroll(oldOff-offset(), 0);
+ else
+ scroll(0, oldOff-offset());
+}
+
+
+
+/*
+ Returns the position of actual division line \a i in widget
+ coordinates. May return a position outside the widget.
+
+ Note that the last division line is numbered count(). (There is one
+ more line than the number of sections).
+*/
+int Q3Header::pPos(int i) const
+{
+ int pos;
+ if (i == count())
+ pos = d->lastPos;
+ else
+ pos = d->positions[i];
+ if (reverse())
+ pos = d->lastPos - pos;
+ return pos - offset();
+}
+
+
+/*
+ Returns the size of the section at index position \a i.
+*/
+int Q3Header::pSize(int i) const
+{
+ return d->sizes[d->i2s[i]];
+}
+
+/*!
+ Use mapToSection() instead.
+
+ Translates from actual index \a a (index at which the section is displayed) to
+ logical index of the section. Returns -1 if \a a is outside the legal range.
+
+ \sa mapToActual()
+*/
+
+int Q3Header::mapToLogical(int a) const
+{
+ return mapToSection(a);
+}
+
+
+/*!
+ Use mapToIndex() instead.
+
+ Translates from logical index \a l to actual index (index at which the section \a l is displayed) .
+ Returns -1 if \a l is outside the legal range.
+
+ \sa mapToLogical()
+*/
+
+int Q3Header::mapToActual(int l) const
+{
+ return mapToIndex(l);
+}
+
+
+/*!
+ Use resizeSection() instead.
+
+ Sets the size of the section \a section to \a s pixels.
+
+ \warning does not repaint or send out signals
+*/
+
+void Q3Header::setCellSize(int section, int s)
+{
+ if (section < 0 || section >= count())
+ return;
+ d->sizes[section] = s;
+ if (updatesEnabled())
+ calculatePositions();
+ else
+ d->positionsDirty = true;
+}
+
+
+/*!
+ If \a enable is true the user may resize section \a section;
+ otherwise the section may not be manually resized.
+
+ If \a section is negative (the default) then the \a enable value
+ is set for all existing sections and will be applied to any new
+ sections that are added.
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3header.cpp 0
+
+ If the user resizes a section, a sizeChange() signal is emitted.
+
+ \sa setMovingEnabled() setClickEnabled() setTracking()
+*/
+
+void Q3Header::setResizeEnabled(bool enable, int section)
+{
+ if (section < 0) {
+ d->resize.fill(enable);
+ // and future ones...
+ d->resize_default = enable;
+ } else if (section < count()) {
+ d->resize[section] = enable;
+ }
+}
+
+
+/*!
+ \property Q3Header::moving
+ \brief whether the header sections can be moved
+
+ If this property is true (the default) the user can move sections.
+ If the user moves a section the indexChange() signal is emitted.
+
+ \sa setClickEnabled(), setResizeEnabled()
+*/
+
+void Q3Header::setMovingEnabled(bool enable)
+{
+ d->move = enable;
+}
+
+
+/*!
+ If \a enable is true, any clicks on section \a section will result
+ in clicked() signals being emitted; otherwise the section will
+ ignore clicks.
+
+ If \a section is -1 (the default) then the \a enable value is set
+ for all existing sections and will be applied to any new sections
+ that are added.
+
+ \sa setMovingEnabled(), setResizeEnabled()
+*/
+
+void Q3Header::setClickEnabled(bool enable, int section)
+{
+ if (section < 0) {
+ d->clicks.fill(enable);
+ // and future ones...
+ d->clicks_default = enable;
+ } else if (section < count()) {
+ d->clicks[section] = enable;
+ }
+}
+
+
+/*!
+ Paints the section at position \a index, inside rectangle \a fr
+ (which uses widget coordinates) using painter \a p.
+
+ Calls paintSectionLabel().
+*/
+
+void Q3Header::paintSection(QPainter *p, int index, const QRect& fr)
+{
+ int section = mapToSection(index);
+ QStyleOptionHeader opt = getStyleOption(this, section);
+ opt.state |= QStyle::State_Raised;
+ opt.rect = fr;
+
+ if (section < 0) {
+ style()->drawControl(QStyle::CE_Header, &opt, p, this);
+ return;
+ }
+
+ if (sectionSize(section) <= 0)
+ return;
+
+ opt.state = (orient == Qt::Horizontal ? QStyle::State_Horizontal : QStyle::State_None);
+ if (d->sortSection == section)
+ opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+
+ if (isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ if (isClickEnabled(section) && (state == Pressed || state == Moving) && index == handleIdx)
+ opt.state |= QStyle::State_Sunken; //currently pressed
+ if (!(opt.state & QStyle::State_Sunken))
+ opt.state |= QStyle::State_Raised;
+ p->setBrushOrigin(fr.topLeft());
+ if (d->clicks[section]) {
+ style()->drawControl(QStyle::CE_Header, &opt, p, this);
+ } else {
+ p->save();
+ p->setClipRect(fr); // hack to keep styles working
+ opt.rect.setRect(fr.x() + 1, fr.y(), fr.width(), fr.height());
+ style()->drawControl(QStyle::CE_Header, &opt, p, this);
+ if (orient == Qt::Horizontal) {
+ p->setPen(palette().color(QPalette::Mid));
+ p->drawLine(fr.x() - 1, fr.y() + fr.height() - 1,
+ fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
+ p->drawLine(fr.x() + fr.width() - 1, fr.y(),
+ fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
+ } else {
+ p->setPen(palette().color(QPalette::Mid));
+ p->drawLine(fr.x() + width() - 1, fr.y(),
+ fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
+ p->drawLine(fr.x(), fr.y() + fr.height() - 1,
+ fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
+ p->setPen(palette().color(QPalette::Light));
+ if (index > 0)
+ p->drawLine(fr.x(), fr.y(), fr.x() + fr.width() - 1, fr.y());
+ if (index == count() - 1) {
+ p->drawLine(fr.x(), fr.y() + fr.height() - 1,
+ fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
+ p->setPen(palette().color(QPalette::Mid));
+ p->drawLine(fr.x(), fr.y() + fr.height() - 2,
+ fr.x() + fr.width() - 1, fr.y() + fr.height() - 2);
+ }
+ }
+ p->restore();
+ }
+}
+
+/*!
+ Paints the label of the section at position \a index, inside
+ rectangle \a fr (which uses widget coordinates) using painter \a
+ p.
+
+ Called by paintSection()
+*/
+void Q3Header::paintSectionLabel(QPainter *p, int index, const QRect& fr)
+{
+ int section = mapToSection(index);
+ if (section < 0)
+ return;
+
+ int dx = 0, dy = 0;
+ QStyleOptionHeader opt = getStyleOption(this, section);
+ if (d->sortSection == section)
+ opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+ if (index == handleIdx && (state == Pressed || state == Moving)) {
+ dx = style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &opt, this);
+ dy = style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &opt, this);
+ opt.state |= QStyle::State_Sunken;
+ }
+ if (isEnabled())
+ opt.state |= QStyle::State_Enabled;
+
+
+ opt.rect.setRect(fr.x() + style()->pixelMetric(QStyle::PM_HeaderMargin) + dx, fr.y() + 2 + dy,
+ fr.width() - 6, fr.height() - 4);
+
+ style()->drawControl(QStyle::CE_HeaderLabel, &opt, p, this);
+
+ int arrowWidth = (orient == Qt::Horizontal ? height() : width()) / 2;
+ int arrowHeight = fr.height() - 6;
+ QSize ssh = sectionSizeHint(section, p->fontMetrics());
+ int tw = (orient == Qt::Horizontal ? ssh.width() : ssh.height());
+ int ew = 0;
+
+ if (style()->styleHint(QStyle::SH_Header_ArrowAlignment, 0, this) & Qt::AlignRight)
+ ew = fr.width() - tw - 8;
+ if (d->sortSection == section && tw <= fr.width()) {
+ if (reverse()) {
+ tw = fr.width() - tw;
+ ew = fr.width() - ew - tw;
+ }
+ opt.state = QStyle::State_None;
+ if (isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ if (d->sortDirection)
+ opt.state |= QStyle::State_DownArrow;
+ else
+ opt.state |= QStyle::State_UpArrow;
+ QRect ar(fr.x() + tw - arrowWidth - 6 + ew, 4, arrowWidth, arrowHeight);
+ if (label(section).isRightToLeft())
+ ar.moveBy( 2*(fr.right() - ar.right()) + ar.width() - fr.width(), 0 );
+ opt.rect = ar;
+ style()->drawPrimitive(QStyle::PE_IndicatorHeaderArrow, &opt, p, this);
+ }
+}
+
+
+/*! \reimp */
+void Q3Header::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ p.setPen(palette().buttonText().color());
+ int pos = orient == Qt::Horizontal ? e->rect().left() : e->rect().top();
+ int id = mapToIndex(sectionAt(pos + offset()));
+ if (id < 0) {
+ if (pos > 0)
+ id = d->count;
+ else if (reverse())
+ id = d->count - 1;
+ else
+ id = 0;
+ }
+ if (reverse()) {
+ for (int i = id; i >= 0; i--) {
+ QRect r = sRect(i);
+ paintSection(&p, i, r);
+ if (r.right() >= e->rect().right())
+ return;
+ }
+ } else {
+ if (count() > 0) {
+ for (int i = id; i <= count(); i++) {
+ QRect r = sRect(i);
+ /*
+ If the last section is clickable (and thus is
+ painted raised), draw the virtual section count()
+ as well. Otherwise it looks ugly.
+ */
+ if (i < count() || d->clicks[mapToSection(count() - 1)])
+ paintSection(&p, i, r);
+ if (hasFocus() && d->focusIdx == i) {
+ QStyleOptionFocusRect opt;
+ opt.rect.setRect(r.x()+2, r.y()+2, r.width()-4, r.height()-4);
+ opt.palette = palette();
+ opt.state = QStyle::State_None;
+ style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this);
+ }
+ if ((orient == Qt::Horizontal && r. right() >= e->rect().right())
+ || (orient == Qt::Vertical && r. bottom() >= e->rect().bottom()))
+ return;
+ }
+ }
+ }
+}
+
+/*!
+ \overload
+
+ Sets the sort indicator to \a ascending. Use the other overload instead.
+*/
+
+void Q3Header::setSortIndicator(int section, bool ascending)
+{
+ d->sortSection = section;
+ if (section != -1)
+ oldHandleIdx = section;
+ d->sortDirection = ascending;
+ update();
+ updateGeometry();
+}
+
+/*!
+ \fn void Q3Header::setSortIndicator(int section, Qt::SortOrder order)
+
+ Sets a sort indicator onto the specified \a section. The indicator's
+ \a order is either Ascending or Descending.
+
+ Only one section can show a sort indicator at any one time. If you
+ don't want any section to show a sort indicator pass a \a section
+ number of -1.
+
+ \sa sortIndicatorSection(), sortIndicatorOrder()
+*/
+
+/*!
+ Returns the section showing the sort indicator or -1 if there is no sort indicator.
+
+ \sa setSortIndicator(), sortIndicatorOrder()
+*/
+
+int Q3Header::sortIndicatorSection() const
+{
+ return d->sortSection;
+}
+
+/*!
+ Returns the implied sort order of the Q3Headers sort indicator.
+
+ \sa setSortIndicator(), sortIndicatorSection()
+*/
+
+Qt::SortOrder Q3Header::sortIndicatorOrder() const
+{
+ return d->sortDirection ? Qt::AscendingOrder : Qt::DescendingOrder;
+}
+
+/*!
+ Resizes section \a section to \a s pixels wide (or high).
+*/
+
+void Q3Header::resizeSection(int section, int s)
+{
+ setCellSize(section, s);
+ update();
+}
+
+/*!
+ Returns the width (or height) of the \a section in pixels.
+*/
+
+int Q3Header::sectionSize(int section) const
+{
+ if (section < 0 || section >= count())
+ return 0;
+ return d->sizes[section];
+}
+
+/*!
+ Returns the position (in pixels) at which the \a section starts.
+
+ \sa offset()
+*/
+
+int Q3Header::sectionPos(int section) const
+{
+ if (d->positionsDirty)
+ ((Q3Header *)this)->calculatePositions();
+ if (section < 0 || section >= count() )
+ return 0;
+ return d->positions[d->s2i[section]];
+}
+
+/*!
+ Returns the index of the section which contains the position \a
+ pos given in pixels from the left (or top).
+
+ \sa offset()
+*/
+
+int Q3Header::sectionAt(int pos) const
+{
+ if (reverse())
+ pos = d->lastPos - pos;
+ return d->sectionAt(pos);
+}
+
+/*!
+ Returns the number of the section that is displayed at index
+ position \a index.
+*/
+
+int Q3Header::mapToSection(int index) const
+{
+ return (index >= 0 && index < count()) ? d->i2s[index] : -1;
+}
+
+/*!
+ Returns the index position at which section \a section is
+ displayed.
+*/
+
+int Q3Header::mapToIndex(int section) const
+{
+ return (section >= 0 && section < count()) ? d->s2i[section] : -1;
+}
+
+/*!
+ Moves section \a section to index position \a toIndex.
+*/
+
+void Q3Header::moveSection(int section, int toIndex)
+{
+ int fromIndex = mapToIndex(section);
+ if (fromIndex == toIndex ||
+ fromIndex < 0 || fromIndex > count() ||
+ toIndex < 0 || toIndex > count())
+ return;
+ int i;
+ int idx = d->i2s[fromIndex];
+ if (fromIndex < toIndex) {
+ for (i = fromIndex; i < toIndex - 1; i++) {
+ int t;
+ d->i2s[i] = t = d->i2s[i+1];
+ d->s2i[t] = i;
+ }
+ d->i2s[toIndex-1] = idx;
+ d->s2i[idx] = toIndex-1;
+ } else {
+ for (i = fromIndex; i > toIndex; i--) {
+ int t;
+ d->i2s[i] = t = d->i2s[i-1];
+ d->s2i[t] = i;
+ }
+ d->i2s[toIndex] = idx;
+ d->s2i[idx] = toIndex;
+ }
+ calculatePositions();
+}
+
+/*!
+ Returns true if section \a section is clickable; otherwise returns
+ false.
+
+ If \a section is out of range (negative or larger than count() -
+ 1): returns true if all sections are clickable; otherwise returns
+ false.
+
+ \sa setClickEnabled()
+*/
+
+bool Q3Header::isClickEnabled(int section) const
+{
+ if (section >= 0 && section < count()) {
+ return (bool)d->clicks[section];
+ }
+
+ for (int i = 0; i < count(); ++i) {
+ if (!d->clicks[i])
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns true if section \a section is resizeable; otherwise
+ returns false.
+
+ If \a section is -1 then this function applies to all sections,
+ i.e. returns true if all sections are resizeable; otherwise
+ returns false.
+
+ \sa setResizeEnabled()
+*/
+
+bool Q3Header::isResizeEnabled(int section) const
+{
+ if (section >= 0 && section < count()) {
+ return (bool)d->resize[section];
+ }
+
+ for (int i = 0; i < count();++i) {
+ if (!d->resize[i])
+ return false;
+ }
+ return true;
+}
+
+bool Q3Header::isMovingEnabled() const
+{
+ return d->move;
+}
+
+/*! \internal */
+
+void Q3Header::setUpdatesEnabled(bool enable)
+{
+ if (enable)
+ calculatePositions();
+ QWidget::setUpdatesEnabled(enable);
+}
+
+
+bool Q3Header::reverse () const
+{
+#if 0
+ return (orient == Qt::Horizontal && QApplication::reverseLayout());
+#else
+ return false;
+#endif
+}
+
+/*! \reimp */
+void Q3Header::resizeEvent(QResizeEvent *e)
+{
+ if (e)
+ QWidget::resizeEvent(e);
+
+ if(d->lastPos < width()) {
+ offs = 0;
+ }
+
+ if (e) {
+ adjustHeaderSize(orientation() == Qt::Horizontal ?
+ width() - e->oldSize().width() : height() - e->oldSize().height());
+ if ((orientation() == Qt::Horizontal && height() != e->oldSize().height())
+ || (orientation() == Qt::Vertical && width() != e->oldSize().width()))
+ update();
+ } else
+ adjustHeaderSize();
+}
+
+/*!
+ \fn void Q3Header::adjustHeaderSize()
+
+ Adjusts the size of the sections to fit the size of the header as
+ completely as possible. Only sections for which isStretchEnabled()
+ is true will be resized.
+*/
+
+void Q3Header::adjustHeaderSize(int diff)
+{
+ if (!count())
+ return;
+
+ // we skip the adjustHeaderSize when trying to resize the last column which is set to stretchable
+ if (d->fullSize == (count() -1) &&
+ (d->lastPos - d->sizes[count() -1]) > (orient == Qt::Horizontal ? width() : height()))
+ return;
+
+ if (d->fullSize >= 0) {
+ int sec = mapToSection(d->fullSize);
+ int lsec = mapToSection(count() - 1);
+ int ns = sectionSize(sec) +
+ (orientation() == Qt::Horizontal ?
+ width() : height()) - (sectionPos(lsec) + sectionSize(lsec));
+ int os = sectionSize(sec);
+ if (ns < 20)
+ ns = 20;
+ setCellSize(sec, ns);
+ repaint();
+ emit sizeChange(sec, os, ns);
+ } else if (d->fullSize == -1) {
+ int df = diff / count();
+ int part = orientation() == Qt::Horizontal ? width() / count() : height() / count();
+ for (int i = 0; i < count() - 1; ++i) {
+ int sec = mapToIndex(i);
+ int os = sectionSize(sec);
+ int ns = diff != -1 ? os + df : part;
+ if (ns < 20)
+ ns = 20;
+ setCellSize(sec, ns);
+ emit sizeChange(sec, os, ns);
+ }
+ int sec = mapToIndex(count() - 1);
+ int ns = (orientation() == Qt::Horizontal ? width() : height()) - sectionPos(sec);
+ int os = sectionSize(sec);
+ if (ns < 20)
+ ns = 20;
+ setCellSize(sec, ns);
+ repaint();
+ emit sizeChange(sec, os, ns);
+ }
+}
+
+/*!
+ Returns the total width of all the header columns.
+*/
+int Q3Header::headerWidth() const
+{
+ if (d->pos_dirty) {
+ ((Q3Header*)this)->calculatePositions();
+ d->pos_dirty = false;
+ }
+ return d->lastPos;
+}
+
+void Q3Header::calculatePositions(bool onlyVisible, int start)
+{
+ d->positionsDirty = false;
+ d->lastPos = count() > 0 ? d->positions[start] : 0;
+ for (int i = start; i < count(); i++) {
+ d->positions[i] = d->lastPos;
+ d->lastPos += d->sizes[d->i2s[i]];
+ if (onlyVisible && d->lastPos > offset() +
+ (orientation() == Qt::Horizontal ? width() : height()))
+ break;
+ }
+ d->pos_dirty = onlyVisible;
+}
+
+
+/*!
+ \property Q3Header::stretching
+ \brief whether the header sections always take up the full width
+ (or height) of the header
+*/
+
+
+/*!
+ If \a b is true, section \a section will be resized when the
+ header is resized, so that the sections take up the full width (or
+ height for vertical headers) of the header; otherwise section \a
+ section will be set to be unstretchable and will not resize when
+ the header is resized.
+
+ If \a section is -1, and if \a b is true, then all sections will
+ be resized equally when the header is resized so that they take up
+ the full width (or height for vertical headers) of the header;
+ otherwise all the sections will be set to be unstretchable and
+ will not resize when the header is resized.
+
+ \sa adjustHeaderSize()
+*/
+
+void Q3Header::setStretchEnabled(bool b, int section)
+{
+ if (b)
+ d->fullSize = section;
+ else
+ d->fullSize = -2;
+ adjustHeaderSize();
+}
+
+bool Q3Header::isStretchEnabled() const
+{
+ return d->fullSize == -1;
+}
+
+/*!
+ \overload
+
+ Returns true if section \a section will resize to take up the full
+ width (or height) of the header; otherwise returns false. If at
+ least one section has stretch enabled the sections will always
+ take up the full width of the header.
+
+ \sa setStretchEnabled()
+*/
+
+bool Q3Header::isStretchEnabled(int section) const
+{
+ return d->fullSize == section;
+}
+
+/*!
+ \reimp
+*/
+void Q3Header::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::FontChange) {
+ QFontMetrics fm = fontMetrics();
+ d->height = (orient == Qt::Horizontal) ? fm.lineSpacing() + 6 : fm.width(QLatin1Char(' '));
+ }
+ QWidget::changeEvent(ev);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HEADER
diff --git a/src/qt3support/widgets/q3header.h b/src/qt3support/widgets/q3header.h
new file mode 100644
index 0000000..a7ac1c9
--- /dev/null
+++ b/src/qt3support/widgets/q3header.h
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3HEADER_H
+#define Q3HEADER_H
+
+#include <QtGui/qicon.h>
+#include <QtGui/qwidget.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_HEADER
+
+class QShowEvent;
+class Q3HeaderData;
+class Q3Table;
+class Q3ListView;
+
+class Q_COMPAT_EXPORT Q3Header : public QWidget
+{
+ friend class Q3Table;
+ friend class Q3TableHeader;
+ friend class Q3ListView;
+
+ Q_OBJECT
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+ Q_PROPERTY(bool tracking READ tracking WRITE setTracking)
+ Q_PROPERTY(int count READ count)
+ Q_PROPERTY(int offset READ offset WRITE setOffset)
+ Q_PROPERTY(bool moving READ isMovingEnabled WRITE setMovingEnabled)
+ Q_PROPERTY(bool stretching READ isStretchEnabled WRITE setStretchEnabled)
+
+public:
+ Q3Header(QWidget* parent=0, const char* name=0);
+ Q3Header(int, QWidget* parent=0, const char* name=0);
+ ~Q3Header();
+
+ int addLabel(const QString &, int size = -1);
+ int addLabel(const QIcon&, const QString &, int size = -1);
+ void removeLabel(int section);
+ virtual void setLabel(int, const QString &, int size = -1);
+ virtual void setLabel(int, const QIcon&, const QString &, int size = -1);
+ QString label(int section) const;
+ QIcon* iconSet(int section) const;
+
+ virtual void setOrientation(Qt::Orientation);
+ Qt::Orientation orientation() const;
+ virtual void setTracking(bool enable);
+ bool tracking() const;
+
+ virtual void setClickEnabled(bool, int section = -1);
+ virtual void setResizeEnabled(bool, int section = -1);
+ virtual void setMovingEnabled(bool);
+ virtual void setStretchEnabled(bool b, int section);
+ void setStretchEnabled(bool b) { setStretchEnabled(b, -1); }
+ bool isClickEnabled(int section = -1) const;
+ bool isResizeEnabled(int section = -1) const;
+ bool isMovingEnabled() const;
+ bool isStretchEnabled() const;
+ bool isStretchEnabled(int section) const;
+
+ void resizeSection(int section, int s);
+ int sectionSize(int section) const;
+ int sectionPos(int section) const;
+ int sectionAt(int pos) const;
+ int count() const;
+ int headerWidth() const;
+ QRect sectionRect(int section) const;
+
+ virtual void setCellSize(int , int); // obsolete, do not use
+ int cellSize(int i) const { return sectionSize(mapToSection(i)); } // obsolete, do not use
+ int cellPos(int) const; // obsolete, do not use
+ int cellAt(int pos) const { return mapToIndex(sectionAt(pos + offset())); } // obsolete, do not use
+
+ int offset() const;
+
+ QSize sizeHint() const;
+
+ int mapToSection(int index) const;
+ int mapToIndex(int section) const;
+ int mapToLogical(int) const; // obsolete, do not use
+ int mapToActual(int) const; // obsolete, do not use
+
+ void moveSection(int section, int toIndex);
+ virtual void moveCell(int, int); // obsolete, do not use
+
+ void setSortIndicator(int section, bool ascending = true); // obsolete, do not use
+ inline void setSortIndicator(int section, Qt::SortOrder order)
+ { setSortIndicator(section, (order == Qt::AscendingOrder)); }
+ int sortIndicatorSection() const;
+ Qt::SortOrder sortIndicatorOrder() const;
+
+ void adjustHeaderSize() { adjustHeaderSize(-1); }
+
+public Q_SLOTS:
+ void setUpdatesEnabled(bool enable);
+ virtual void setOffset(int pos);
+
+Q_SIGNALS:
+ void clicked(int section);
+ void pressed(int section);
+ void released(int section);
+ void sizeChange(int section, int oldSize, int newSize);
+ void indexChange(int section, int fromIndex, int toIndex);
+ void sectionClicked(int); // obsolete, do not use
+ void moved(int, int); // obsolete, do not use
+ void sectionHandleDoubleClicked(int section);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void showEvent(QShowEvent *e);
+ void resizeEvent(QResizeEvent *e);
+ QRect sRect(int index);
+
+ virtual void paintSection(QPainter *p, int index, const QRect& fr);
+ virtual void paintSectionLabel(QPainter* p, int index, const QRect& fr);
+
+ void changeEvent(QEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mouseDoubleClickEvent(QMouseEvent *);
+
+ void keyPressEvent(QKeyEvent *);
+ void keyReleaseEvent(QKeyEvent *);
+
+private:
+ void handleColumnMove(int fromIdx, int toIdx);
+ void adjustHeaderSize(int diff);
+ void init(int);
+
+ void paintRect(int p, int s);
+ void markLine(int idx);
+ void unMarkLine(int idx);
+ int pPos(int i) const;
+ int pSize(int i) const;
+ int findLine(int);
+ int handleAt(int p);
+ bool reverse() const;
+ void calculatePositions(bool onlyVisible = false, int start = 0);
+ void handleColumnResize(int, int, bool, bool = true);
+ QSize sectionSizeHint(int section, const QFontMetrics& fm) const;
+ void setSectionSizeAndHeight(int section, int size);
+
+ void resizeArrays(int size);
+ void setIsATableHeader(bool b);
+ int offs;
+ int handleIdx;
+ int oldHIdxSize;
+ int moveToIdx;
+ enum State { Idle, Sliding, Pressed, Moving, Blocked };
+ State state;
+ int clickPos;
+ bool trackingIsOn;
+ int oldHandleIdx;
+ int cachedPos; // not used
+ Qt::Orientation orient;
+
+ Q3HeaderData *d;
+
+private:
+ Q_DISABLE_COPY(Q3Header)
+};
+
+
+inline Qt::Orientation Q3Header::orientation() const
+{
+ return orient;
+}
+
+inline void Q3Header::setTracking(bool enable) { trackingIsOn = enable; }
+inline bool Q3Header::tracking() const { return trackingIsOn; }
+
+extern Q_COMPAT_EXPORT bool qt_qheader_label_return_null_strings; // needed for professional edition
+
+#endif // QT_NO_HEADER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3HEADER_H
diff --git a/src/qt3support/widgets/q3hgroupbox.cpp b/src/qt3support/widgets/q3hgroupbox.cpp
new file mode 100644
index 0000000..f5672a0
--- /dev/null
+++ b/src/qt3support/widgets/q3hgroupbox.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3hgroupbox.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3HGroupBox
+
+ \brief The Q3HGroupBox widget organizes widgets in a group with one
+ horizontal row.
+
+ \compat
+
+ Q3HGroupBox is a convenience class that offers a thin layer on top
+ of Q3GroupBox. Think of it as a Q3HBox that offers a frame with a
+ title.
+
+ \sa Q3VGroupBox
+*/
+
+/*!
+ Constructs a horizontal group box with no title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+Q3HGroupBox::Q3HGroupBox( QWidget *parent, const char *name )
+ : Q3GroupBox( 1, Qt::Vertical /* sic! */, parent, name )
+{
+}
+
+/*!
+ Constructs a horizontal group box with the title \a title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3HGroupBox::Q3HGroupBox( const QString &title, QWidget *parent,
+ const char *name )
+ : Q3GroupBox( 1, Qt::Vertical /* sic! */, title, parent, name )
+{
+}
+
+/*!
+ Destroys the horizontal group box, deleting its child widgets.
+*/
+Q3HGroupBox::~Q3HGroupBox()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3hgroupbox.h b/src/qt3support/widgets/q3hgroupbox.h
new file mode 100644
index 0000000..b2c8ebd
--- /dev/null
+++ b/src/qt3support/widgets/q3hgroupbox.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3HGROUPBOX_H
+#define Q3HGROUPBOX_H
+
+#include <Qt3Support/q3groupbox.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3HGroupBox : public Q3GroupBox
+{
+ Q_OBJECT
+public:
+ Q3HGroupBox( QWidget* parent=0, const char* name=0 );
+ Q3HGroupBox( const QString &title, QWidget* parent=0, const char* name=0 );
+ ~Q3HGroupBox();
+
+private:
+ Q_DISABLE_COPY(Q3HGroupBox)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3HGROUPBOX_H
diff --git a/src/qt3support/widgets/q3mainwindow.cpp b/src/qt3support/widgets/q3mainwindow.cpp
new file mode 100644
index 0000000..6e066d3
--- /dev/null
+++ b/src/qt3support/widgets/q3mainwindow.cpp
@@ -0,0 +1,2427 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3mainwindow.h"
+#ifndef QT_NO_MAINWINDOW
+
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qdatetime.h"
+#include "q3dockarea.h"
+#include "qevent.h"
+#include "qlayout.h"
+#include "qmap.h"
+#include "qmenubar.h"
+#include "qpainter.h"
+#include "q3popupmenu.h"
+#include "q3scrollview.h"
+#include "qstatusbar.h"
+#include "qstringlist.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "q3toolbar.h"
+#include "qtooltip.h"
+#include "qwhatsthis.h"
+#ifdef Q_WS_MAC
+# include <private/qt_mac_p.h>
+#endif
+
+#include <private/q3mainwindow_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHideDock;
+
+/* Q3MainWindowLayout, respects widthForHeight layouts (like the left
+ and right docks are)
+*/
+
+class Q3MainWindowLayout : public QLayout
+{
+ Q_OBJECT
+
+public:
+ Q3MainWindowLayout(Q3MainWindow *mw);
+ ~Q3MainWindowLayout() {}
+
+ void addItem(QLayoutItem *);
+ void setLeftDock(Q3DockArea *l);
+ void setRightDock(Q3DockArea *r);
+ void setCentralWidget(QWidget *w);
+ bool hasHeightForWidth() const { return false; }
+ QSize sizeHint() const;
+ QSize minimumSize() const;
+ QLayoutItem *itemAt(int) const { return 0; } //###
+ QLayoutItem *takeAt(int) { return 0; } //###
+ int count() const { return 0; } //###
+
+protected:
+ void setGeometry(const QRect &r) {
+ QLayout::setGeometry(r);
+ layoutItems(r);
+ }
+
+private:
+ int layoutItems(const QRect&, bool testonly = false);
+ int extraPixels() const;
+
+ Q3DockArea *left, *right;
+ QWidget *central;
+ Q3MainWindow *mainWindow;
+
+};
+
+QSize Q3MainWindowLayout::sizeHint() const
+{
+ int w = 0;
+ int h = 0;
+
+ if (left) {
+ w += left->sizeHint().width();
+ h = qMax(h, left->sizeHint().height());
+ }
+ if (right) {
+ w += right->sizeHint().width();
+ h = qMax(h, right->sizeHint().height());
+ }
+ if (central) {
+ w += central->sizeHint().width();
+ int diff = extraPixels();
+ h = qMax(h, central->sizeHint().height() + diff);
+ }
+ return QSize(w, h);
+}
+
+QSize Q3MainWindowLayout::minimumSize() const
+{
+ int w = 0;
+ int h = 0;
+
+ if (left) {
+ QSize ms = left->minimumSizeHint().expandedTo(left->minimumSize());
+ w += ms.width();
+ h = qMax(h, ms.height());
+ }
+ if (right) {
+ QSize ms = right->minimumSizeHint().expandedTo(right->minimumSize());
+ w += ms.width();
+ h = qMax(h, ms.height());
+ }
+ if (central) {
+ QSize min = central->minimumSize().isNull() ?
+ central->minimumSizeHint() : central->minimumSize();
+ w += min.width();
+ int diff = extraPixels();
+ h = qMax(h, min.height() + diff);
+ }
+ return QSize(w, h);
+}
+
+Q3MainWindowLayout::Q3MainWindowLayout(Q3MainWindow *mw)
+ : left(0), right(0), central(0)
+{
+ mainWindow = mw;
+}
+
+void Q3MainWindowLayout::setLeftDock(Q3DockArea *l)
+{
+ left = l;
+}
+
+void Q3MainWindowLayout::setRightDock(Q3DockArea *r)
+{
+ right = r;
+}
+
+void Q3MainWindowLayout::setCentralWidget(QWidget *w)
+{
+ central = w;
+}
+
+int Q3MainWindowLayout::layoutItems(const QRect &r, bool testonly)
+{
+ if (!left && !central && !right)
+ return 0;
+
+ int wl = 0, wr = 0;
+ if (left)
+ wl = ((Q3DockAreaLayout*)left->QWidget::layout())->widthForHeight(r.height());
+ if (right)
+ wr = ((Q3DockAreaLayout*)right->QWidget::layout())->widthForHeight(r.height());
+ int w = r.width() - wr - wl;
+ if (w < 0)
+ w = 0;
+
+ int diff = extraPixels();
+ if (!testonly) {
+ QRect g(geometry());
+ if (left)
+ left->setGeometry(QRect(g.x(), g.y() + diff, wl, r.height() - diff));
+ if (right)
+ right->setGeometry(QRect(g.x() + g.width() - wr, g.y() + diff, wr, r.height() - diff));
+ if (central)
+ central->setGeometry(g.x() + wl, g.y() + diff, w, r.height() - diff);
+ }
+
+ w = wl + wr;
+ if (central)
+ w += central->minimumSize().width();
+ return w;
+}
+
+int Q3MainWindowLayout::extraPixels() const
+{
+ if (mainWindow->d_func()->topDock->isEmpty() &&
+ !(mainWindow->d_func()->leftDock->isEmpty() &&
+ mainWindow->d_func()->rightDock->isEmpty())) {
+ return 2;
+ } else {
+ return 0;
+ }
+}
+
+void Q3MainWindowLayout::addItem(QLayoutItem * /* item */)
+{
+}
+
+/*
+ QHideToolTip and QHideDock - minimized dock
+*/
+
+#if 0
+class QHideToolTip : public QToolTip
+{
+public:
+ QHideToolTip(QWidget *parent) : QToolTip(parent) {}
+
+ void maybeTip(const QPoint &pos);
+};
+#endif
+
+
+class QHideDock : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QHideDock(Q3MainWindow *parent) : QWidget(parent, "qt_hide_dock") {
+ hide();
+ setFixedHeight(style()->pixelMetric(QStyle::PM_DockWidgetHandleExtent, 0, this) + 3);
+ pressedHandle = -1;
+ pressed = false;
+ setMouseTracking(true);
+ win = parent;
+#if 0
+ tip = new QHideToolTip(this);
+#endif
+ }
+ ~QHideDock()
+ {
+#if 0
+ delete tip;
+#endif
+ }
+
+protected:
+ void paintEvent(QPaintEvent *e) {
+ QObjectList childList = children();
+ if (childList.isEmpty())
+ return;
+ QPainter p(this);
+ p.setClipRegion(e->rect());
+ p.fillRect(e->rect(), palette().brush(QPalette::Window));
+ int x = 0;
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *o = childList.at(i);
+ Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o);
+ if (!dw || !dw->isVisible())
+ continue;
+ QStyleOptionQ3DockWindow opt;
+ opt.rect.setRect(x, 0, 30, 10);
+ opt.palette = palette();
+ opt.docked = dw->area();
+ opt.closeEnabled = dw->isCloseEnabled();
+ opt.state = QStyle::State_None;
+ if (i == pressedHandle)
+ opt.state |= QStyle::State_On;
+
+ style()->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this);
+ x += 30;
+ }
+ }
+
+ void mousePressEvent(QMouseEvent *e) {
+ pressed = true;
+ QObjectList childList = children();
+ if (childList.isEmpty())
+ return;
+ mouseMoveEvent(e);
+ pressedHandle = -1;
+
+ if (e->button() == Qt::RightButton && win->isDockMenuEnabled()) {
+ // ### TODO: HideDock menu
+ } else {
+ mouseMoveEvent(e);
+ }
+ }
+
+ void mouseMoveEvent(QMouseEvent *e) {
+ QObjectList childList = children();
+ if (childList.isEmpty())
+ return;
+ if (!pressed)
+ return;
+ int x = 0;
+ if (e->y() >= 0 && e->y() <= height()) {
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *o = childList.at(i);
+ Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o);
+ if (!dw || !dw->isVisible())
+ continue;
+ if (e->x() >= x && e->x() <= x + 30) {
+ int old = pressedHandle;
+ pressedHandle = i;
+ if (pressedHandle != old)
+ repaint();
+ return;
+ }
+ x += 30;
+ }
+ }
+ int old = pressedHandle;
+ pressedHandle = -1;
+ if (old != -1)
+ repaint();
+ }
+
+ void mouseReleaseEvent(QMouseEvent *e) {
+ pressed = false;
+ if (pressedHandle == -1)
+ return;
+ QObjectList childList = children();
+ if (childList.isEmpty())
+ return;
+ if (e->button() == Qt::LeftButton) {
+ if (e->y() >= 0 && e->y() <= height()) {
+ QObject *o = childList.at(pressedHandle);
+ Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o);
+ if (dw) {
+ dw->show();
+ dw->dock();
+ }
+ }
+ }
+ pressedHandle = -1;
+ repaint();
+ }
+
+ bool eventFilter(QObject *o, QEvent *e) {
+ if (o == this || !o->isWidgetType())
+ return QWidget::eventFilter(o, e);
+ if (e->type() == QEvent::HideToParent ||
+ e->type() == QEvent::ShowToParent)
+ updateState();
+ return QWidget::eventFilter(o, e);
+ }
+
+ void updateState() {
+ bool visible = true;
+ QObjectList childList = children();
+ if (childList.isEmpty())
+ return;
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *o = childList.at(i);
+ Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o);
+ if (!dw)
+ continue;
+ if (dw->isHidden()) {
+ visible = false;
+ continue;
+ }
+ if (!dw->isVisible())
+ continue;
+ visible = true;
+ break;
+ }
+
+ if (visible)
+ show();
+ else
+ hide();
+ win->triggerLayout(false);
+ update();
+ }
+
+ void childEvent(QChildEvent *e) {
+ QWidget::childEvent(e);
+ if (e->type() == QEvent::ChildInserted)
+ e->child()->installEventFilter(this);
+ else
+ e->child()->removeEventFilter(this);
+ updateState();
+ }
+
+private:
+ Q3MainWindow *win;
+ int pressedHandle;
+ bool pressed;
+#if 0
+ QHideToolTip *tip;
+ friend class QHideToolTip;
+#endif
+};
+
+#if 0
+void QHideToolTip::maybeTip(const QPoint &pos)
+{
+ if (!parentWidget())
+ return;
+ QHideDock *dock = (QHideDock*)parentWidget();
+
+ QObjectList dchilds = dock->children();
+ if (dchilds.isEmpty())
+ return;
+ int x = 0;
+ for (int i = 0; i < dchilds.size(); ++i) {
+ QObject *o = dchilds.at(i);
+ Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o);
+ if (!dw || !dw->isVisible())
+ continue;
+ if (pos.x() >= x && pos.x() <= x + 30) {
+ Q3DockWindow *dw = (Q3DockWindow*)o;
+ if (!dw->windowTitle().isEmpty())
+ tip(QRect(x, 0, 30, dock->height()), dw->windowTitle());
+ return;
+ }
+ x += 30;
+ }
+}
+#endif
+
+/*!
+ \class Q3MainWindow
+ \brief The Q3MainWindow class provides a main application window,
+ with a menu bar, dock windows (e.g. for toolbars), and a status
+ bar.
+
+ \compat
+
+ Main windows are most often used to provide menus, toolbars and a
+ status bar around a large central widget, such as a text edit,
+ drawing canvas or QWorkspace (for MDI applications). Q3MainWindow
+ is usually subclassed since this makes it easier to encapsulate
+ the central widget, menus and toolbars as well as the window's
+ state. Subclassing makes it possible to create the slots that are
+ called when the user clicks menu items or toolbar buttons.
+
+ We'll briefly review adding menu items and
+ toolbar buttons then describe the facilities of Q3MainWindow
+ itself.
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 0
+
+ Q3MainWindows may be created in their own right as shown above.
+ The central widget is set with setCentralWidget(). Popup menus can
+ be added to the default menu bar, widgets can be added to the
+ status bar, toolbars and dock windows can be added to any of the
+ dock areas.
+
+ The main window will take care of the dock areas, and the geometry
+ of the central widget, but all other aspects of the central widget
+ are left to you. Q3MainWindow automatically detects the creation of
+ a menu bar or status bar if you specify the Q3MainWindow as parent,
+ or you can use the provided menuBar() and statusBar() functions.
+ The functions menuBar() and statusBar() create a suitable widget
+ if one doesn't exist, and update the window's layout to make
+ space.
+
+ New dock windows and toolbars can be added to a Q3MainWindow using
+ addDockWindow(). Qt::Dock windows can be moved using moveDockWindow()
+ and removed with removeDockWindow(). Q3MainWindow allows default
+ dock window (toolbar) docking in all its dock areas (\c Top, \c
+ Left, \c Right, \c Bottom). You can use setDockEnabled() to
+ enable and disable docking areas for dock windows. When adding or
+ moving dock windows you can specify their 'edge' (dock area). The
+ currently available edges are: \c Top, \c Left, \c Right, \c
+ Bottom, \c Minimized (effectively a 'hidden' dock area) and \c
+ TornOff (floating). See \l Qt::Dock for an explanation of these
+ areas. Note that the *ToolBar functions are included for backward
+ compatibility; all new code should use the *DockWindow functions.
+ QToolbar is a subclass of Q3DockWindow so all functions that work
+ with dock windows work on toolbars in the same way.
+
+ \target dwm
+ If the user clicks the close button, then the dock window is
+ hidden. A dock window can be hidden or unhidden by the user by
+ right clicking a dock area and clicking the name of the relevant
+ dock window on the pop up dock window menu. This menu lists the
+ names of every dock window; visible dock windows have a tick
+ beside their names. The dock window menu is created automatically
+ as required by createDockWindowMenu(). Since it may not always be
+ appropriate for a dock window to appear on this menu the
+ setAppropriate() function is used to inform the main window
+ whether or not the dock window menu should include a particular
+ dock window. Double clicking a dock window handle (usually on the
+ left-hand side of the dock window) undocks (floats) the dock
+ window. Double clicking a floating dock window's title bar will
+ dock the floating dock window. (See also
+ \l{Q3MainWindow::DockWindows}.)
+
+ Some functions change the appearance of a Q3MainWindow globally:
+ \list
+ \i Q3DockWindow::setHorizontalStretchable() and
+ Q3DockWindow::setVerticalStretchable() are used to make specific dock
+ windows or toolbars stretchable.
+ \i setUsesBigPixmaps() is used to set whether tool buttons should
+ draw small or large pixmaps (see QIcon for more information).
+ \i setUsesTextLabel() is used to set whether tool buttons
+ should display a textual label in addition to pixmaps
+ (see QToolButton for more information).
+ \endlist
+
+ The user can drag dock windows into any enabled docking area. Qt::Dock
+ windows can also be dragged \e within a docking area, for example
+ to rearrange the order of some toolbars. Qt::Dock windows can also be
+ dragged outside any docking area (undocked or 'floated'). Being
+ able to drag dock windows can be enabled (the default) and
+ disabled using setDockWindowsMovable().
+
+ The \c Minimized edge is a hidden dock area. If this dock area is
+ enabled the user can hide (minimize) a dock window or show (restore)
+ a minimized dock window by clicking the dock window handle. If the
+ user hovers the mouse cursor over one of the handles, the caption of
+ the dock window is displayed in a tool tip (see
+ Q3DockWindow::windowTitle() or Q3ToolBar::label()), so if you enable the
+ \c Minimized dock area, it is best to specify a meaningful caption
+ or label for each dock window. To minimize a dock window
+ programmatically use moveDockWindow() with an edge of \c Minimized.
+
+ Qt::Dock windows are moved transparently by default, i.e. during the
+ drag an outline rectangle is drawn on the screen representing the
+ position of the dock window as it moves. If you want the dock
+ window to be shown normally whilst it is moved use
+ setOpaqueMoving().
+
+ The location of a dock window, i.e. its dock area and position
+ within the dock area, can be determined by calling getLocation().
+ Movable dock windows can be lined up to minimize wasted space with
+ lineUpDockWindows(). Pointers to the dock areas are available from
+ topDock(), leftDock(), rightDock() and bottomDock(). A customize
+ menu item is added to the pop up dock window menu if
+ isCustomizable() returns true; it returns false by default.
+ Reimplement isCustomizable() and customize() if you want to offer
+ this extra menu item, for example, to allow the user to change
+ settings relating to the main window and its toolbars and dock
+ windows.
+
+ The main window's menu bar is fixed (at the top) by default. If
+ you want a movable menu bar, create a QMenuBar as a stretchable
+ widget inside its own movable dock window and restrict this dock
+ window to only live within the \c Top or \c Bottom dock:
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 1
+
+ An application with multiple dock windows can choose to save the
+ current dock window layout in order to restore it later, e.g. in
+ the next session. You can do this by using the streaming operators
+ for Q3MainWindow.
+
+ To save the layout and positions of all the dock windows do this:
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 2
+
+ To restore the dock window positions and sizes (normally when the
+ application is next started), do the following:
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 3
+
+ The QSettings class can be used in conjunction with the streaming
+ operators to store the application's settings.
+
+ Q3MainWindow's management of dock windows and toolbars is done
+ transparently behind-the-scenes by Q3DockArea.
+
+ For multi-document interfaces (MDI), use a QWorkspace as the
+ central widget.
+
+ Adding dock windows, e.g. toolbars, to Q3MainWindow's dock areas is
+ straightforward. If the supplied dock areas are not sufficient for
+ your application we suggest that you create a QWidget subclass and
+ add your own dock areas (see \l Q3DockArea) to the subclass since
+ Q3MainWindow provides functionality specific to the standard dock
+ areas it provides.
+
+ \sa Q3ToolBar Q3DockWindow QStatusBar QAction QMenuBar Q3PopupMenu QDialog
+*/
+
+/*!
+ \enum Q3MainWindow::DockWindows
+
+ Right-clicking a dock area will pop-up the dock window menu
+ (createDockWindowMenu() is called automatically). When called in
+ code you can specify what items should appear on the menu with
+ this enum.
+
+ \value OnlyToolBars The menu will list all the toolbars, but not
+ any other dock windows.
+
+ \value NoToolBars The menu will list dock windows but not
+ toolbars.
+
+ \value AllDockWindows The menu will list all toolbars and other
+ dock windows. (This is the default.)
+*/
+
+/*!
+ \fn void Q3MainWindow::addToolBar(Q3DockWindow *dockWindow,
+ Qt::Dock position, bool newLine);
+
+ Adds a new toolbar to the \a dockWindow. The toolbar is placed in
+ the given \a position. If \a newLine is true the toolbar is put on
+ a new line.
+*/
+
+/*!
+ \fn void Q3MainWindow::addToolBar(Q3DockWindow *dockWindow, const
+ QString &label, Qt::Dock position, bool newLine)
+ \overload
+
+ The toolbar has the caption \a label and is placed in the given \a
+ position.
+*/
+
+/*!
+ \fn void Q3MainWindow::moveToolBar(Q3DockWindow *dockWindow, Qt::Dock position);
+
+ Moves the given \a dockWindow into the given \a position.
+*/
+
+/*!
+ \fn void Q3MainWindow::moveToolBar(Q3DockWindow *dockWindow,
+ Qt::Dock position, bool nl, int index, int extraOffset)
+ \overload
+
+ The \a dockWindow is made the \a{index}-th item in the toolbar,
+ moved over by \a extraOffset. If \a nl is true, the dock window is
+ put on a new line.
+*/
+
+/*!
+ \fn void Q3MainWindow::removeToolBar(Q3DockWindow *dockWindow);
+
+ Removes the toolbar from the given \a dockWindow.
+*/
+
+/*!
+ \fn void Q3MainWindow::lineUpToolBars(bool keepNewLines);
+
+ Lines up the toolbars. Line breaks are preserved if \a
+ keepNewLines is true.
+*/
+
+/*!
+ \fn void Q3MainWindow::toolBarPositionChanged(Q3ToolBar *toolbar);
+
+ This signal is emitted when a \a toolbar is moved.
+*/
+
+/*!
+ \fn bool Q3MainWindow::toolBarsMovable() const
+
+ Returns true if the window allows its toolbars to be moved; otherwise
+ returns false.
+*/
+
+/*!
+ \fn void Q3MainWindow::setToolBarsMovable(bool b)
+ If \a b is true the tool bars can be moved.
+*/
+
+/*!
+ Constructs an empty main window. The \a parent, \a name and widget
+ flags \a f, are passed on to the QWidget constructor.
+
+ By default, the widget flags are set to Qt::WType_TopLevel rather
+ than 0 as they are with QWidget. If you don't want your
+ Q3MainWindow to be a top level widget then you will need to set \a
+ f to 0.
+*/
+
+Q3MainWindow::Q3MainWindow(QWidget * parent, const char * name, Qt::WindowFlags f)
+ : QWidget(*new Q3MainWindowPrivate, parent, f)
+{
+ Q_D(Q3MainWindow);
+ setObjectName(QLatin1String(name));
+#ifdef Q_WS_MAC
+ d->opaque = true;
+#else
+ d->opaque = false;
+#endif
+ installEventFilter(this);
+ d->topDock = new Q3DockArea(Qt::Horizontal, Q3DockArea::Normal, this, "qt_top_dock");
+ d->topDock->installEventFilter(this);
+ d->bottomDock = new Q3DockArea(Qt::Horizontal, Q3DockArea::Reverse, this, "qt_bottom_dock");
+ d->bottomDock->installEventFilter(this);
+ d->leftDock = new Q3DockArea(Qt::Vertical, Q3DockArea::Normal, this, "qt_left_dock");
+ d->leftDock->installEventFilter(this);
+ d->rightDock = new Q3DockArea(Qt::Vertical, Q3DockArea::Reverse, this, "qt_right_dock");
+ d->rightDock->installEventFilter(this);
+ d->hideDock = new QHideDock(this);
+}
+
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3MainWindow::~Q3MainWindow()
+{
+ delete layout();
+}
+
+#ifndef QT_NO_MENUBAR
+/*!
+ Sets this main window to use the menu bar \a newMenuBar.
+
+ The existing menu bar (if any) is deleted along with its contents.
+
+ \sa menuBar()
+*/
+
+void Q3MainWindow::setMenuBar(QMenuBar * newMenuBar)
+{
+ Q_D(Q3MainWindow);
+ if (!newMenuBar)
+ return;
+ if (d->mb)
+ delete d->mb;
+ d->mb = newMenuBar;
+ d->mb->installEventFilter(this);
+ triggerLayout();
+}
+
+
+/*!
+ Returns the menu bar for this window.
+
+ If there isn't one, then menuBar() creates an empty menu bar.
+
+ \sa statusBar()
+*/
+
+QMenuBar * Q3MainWindow::menuBar() const
+{
+ Q_D(const Q3MainWindow);
+ if (d->mb)
+ return d->mb;
+
+ QObjectList l = queryList("QMenuBar", 0, false, false);
+ QMenuBar * b;
+ if (l.size()) {
+ b = static_cast<QMenuBar *>(l.at(0));
+ } else {
+ b = new QMenuBar((Q3MainWindow *)this);
+ b->setObjectName(QLatin1String("automatic menu bar"));
+ b->show();
+ }
+ d->mb = b;
+ d->mb->installEventFilter(const_cast<Q3MainWindow *>(this));
+ ((Q3MainWindow *)this)->triggerLayout();
+ return b;
+}
+#endif // QT_NO_MENUBAR
+
+/*!
+ Sets this main window to use the status bar \a newStatusBar.
+
+ The existing status bar (if any) is deleted along with its
+ contents.
+
+ Note that \a newStatusBar \e must be a child of this main window,
+ and that it is not automatically displayed. If you call this
+ function after show(), you will probably also need to call
+ newStatusBar->show().
+
+ \sa setMenuBar() statusBar()
+*/
+
+void Q3MainWindow::setStatusBar(QStatusBar * newStatusBar)
+{
+ Q_D(Q3MainWindow);
+ if (!newStatusBar || newStatusBar == d->sb)
+ return;
+ if (d->sb)
+ delete d->sb;
+ d->sb = newStatusBar;
+#if 0
+ // ### this code can cause unnecessary creation of a tool tip group
+ connect(toolTipGroup(), SIGNAL(showTip(QString)),
+ d->sb, SLOT(showMessage(QString)));
+ connect(toolTipGroup(), SIGNAL(removeTip()),
+ d->sb, SLOT(clearMessage()));
+#endif
+ d->sb->installEventFilter(this);
+ triggerLayout();
+}
+
+
+/*!
+ Returns this main window's status bar. If there isn't one,
+ statusBar() creates an empty status bar, and if necessary a tool
+ tip group too.
+
+ \sa menuBar()
+*/
+
+QStatusBar * Q3MainWindow::statusBar() const
+{
+ Q_D(const Q3MainWindow);
+ if (d->sb)
+ return d->sb;
+
+ QObjectList l = queryList("QStatusBar", 0, false, false);
+ QStatusBar * s;
+ if (l.size()) {
+ s = (QStatusBar *)l.at(0);
+ } else {
+ s = new QStatusBar((Q3MainWindow *)this, "automatic status bar");
+ s->show();
+ }
+ ((Q3MainWindow *)this)->setStatusBar(s);
+ ((Q3MainWindow *)this)->triggerLayout(true);
+ return s;
+}
+
+
+#if 0
+/*!
+ Sets this main window to use the tool tip group \a
+ newToolTipGroup.
+
+ The existing tool tip group (if any) is deleted along with its
+ contents. All the tool tips connected to it lose the ability to
+ display the group texts.
+
+ \sa menuBar()
+*/
+
+void Q3MainWindow::setToolTipGroup(QToolTipGroup * newToolTipGroup)
+{
+ Q_D(Q3MainWindow);
+ if (!newToolTipGroup || newToolTipGroup == d->ttg)
+ return;
+ if (d->ttg)
+ delete d->ttg;
+ d->ttg = newToolTipGroup;
+
+ connect(toolTipGroup(), SIGNAL(showTip(QString)),
+ statusBar(), SLOT(showMessage(QString)));
+ connect(toolTipGroup(), SIGNAL(removeTip()),
+ statusBar(), SLOT(clearMessage()));
+}
+
+
+/*!
+ Returns this main window's tool tip group. If there isn't one,
+ toolTipGroup() creates an empty tool tip group.
+
+ \sa menuBar() statusBar()
+*/
+QToolTipGroup * Q3MainWindow::toolTipGroup() const
+{
+ Q_D(const Q3MainWindow);
+ if (d->ttg)
+ return d->ttg;
+
+ QToolTipGroup * t = new QToolTipGroup((Q3MainWindow*)this,
+ "automatic tool tip group");
+ ((Q3MainWindowPrivate*)d)->ttg = t;
+ return t;
+}
+#endif
+
+
+/*!
+ If \a enable is true then users can dock windows in the \a dock
+ area. If \a enable is false users cannot dock windows in the \a
+ dock dock area.
+
+ Users can dock (drag) dock windows into any enabled dock area.
+*/
+
+void Q3MainWindow::setDockEnabled(Qt::Dock dock, bool enable)
+{
+ d_func()->docks.insert(dock, enable);
+}
+
+
+/*!
+ Returns true if the \a dock dock area is enabled, i.e. it can
+ accept user dragged dock windows; otherwise returns false.
+
+ \sa setDockEnabled()
+*/
+
+bool Q3MainWindow::isDockEnabled(Qt::Dock dock) const
+{
+ return d_func()->docks[dock];
+}
+
+/*!
+ \overload
+
+ Returns true if dock area \a area is enabled, i.e. it can accept
+ user dragged dock windows; otherwise returns false.
+
+ \sa setDockEnabled()
+*/
+
+bool Q3MainWindow::isDockEnabled(Q3DockArea *area) const
+{
+ Q_D(const Q3MainWindow);
+ if (area == d->leftDock)
+ return d->docks[Qt::DockLeft];
+ if (area == d->rightDock)
+ return d->docks[Qt::DockRight];
+ if (area == d->topDock)
+ return d->docks[Qt::DockTop];
+ if (area == d->bottomDock)
+ return d->docks[Qt::DockBottom];
+ return false;
+}
+
+/*!
+ \overload
+
+ If \a enable is true then users can dock the \a dw dock window in
+ the \a dock area. If \a enable is false users cannot dock the \a
+ dw dock window in the \a dock area.
+
+ In general users can dock (drag) dock windows into any enabled
+ dock area. Using this function particular dock areas can be
+ enabled (or disabled) as docking points for particular dock
+ windows.
+*/
+
+
+void Q3MainWindow::setDockEnabled(Q3DockWindow *dw, Qt::Dock dock, bool enable)
+{
+ Q_D(Q3MainWindow);
+ if (!d->dockWindows.contains(dw)) {
+ d->dockWindows.append(dw);
+ connect(dw, SIGNAL(placeChanged(Q3DockWindow::Place)),
+ this, SLOT(slotPlaceChanged()));
+ }
+ QString s;
+ s.sprintf("%p_%d", (void*)dw, (int)dock);
+ if (enable)
+ d->disabledDocks.removeAll(s);
+ else if (!d->disabledDocks.contains(s))
+ d->disabledDocks << s;
+ switch (dock) {
+ case Qt::DockTop:
+ topDock()->setAcceptDockWindow(dw, enable);
+ break;
+ case Qt::DockLeft:
+ leftDock()->setAcceptDockWindow(dw, enable);
+ break;
+ case Qt::DockRight:
+ rightDock()->setAcceptDockWindow(dw, enable);
+ break;
+ case Qt::DockBottom:
+ bottomDock()->setAcceptDockWindow(dw, enable);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \overload
+
+ Returns true if dock area \a area is enabled for the dock window
+ \a dw; otherwise returns false.
+
+ \sa setDockEnabled()
+*/
+
+bool Q3MainWindow::isDockEnabled(Q3DockWindow *dw, Q3DockArea *area) const
+{
+ Q_D(const Q3MainWindow);
+ if (!isDockEnabled(area))
+ return false;
+ Qt::Dock dock;
+ if (area == d->leftDock)
+ dock = Qt::DockLeft;
+ else if (area == d->rightDock)
+ dock = Qt::DockRight;
+ else if (area == d->topDock)
+ dock = Qt::DockTop;
+ else if (area == d->bottomDock)
+ dock = Qt::DockBottom;
+ else
+ return false;
+ return isDockEnabled(dw, dock);
+}
+
+/*!
+ \overload
+
+ Returns true if dock area \a dock is enabled for the dock window
+ \a tb; otherwise returns false.
+
+ \sa setDockEnabled()
+*/
+
+bool Q3MainWindow::isDockEnabled(Q3DockWindow *tb, Qt::Dock dock) const
+{
+ if (!isDockEnabled(dock))
+ return false;
+ QString s;
+ s.sprintf("%p_%d", (void*)tb, (int)dock);
+ return !d_func()->disabledDocks.contains(s);
+}
+
+
+
+/*!
+ Adds \a dockWindow to the \a edge dock area.
+
+ If \a newLine is false (the default) then the \a dockWindow is
+ added at the end of the \a edge. For vertical edges the end is at
+ the bottom, for horizontal edges (including \c Minimized) the end
+ is at the right. If \a newLine is true a new line of dock windows
+ is started with \a dockWindow as the first (left-most and
+ top-most) dock window.
+
+ If \a dockWindow is managed by another main window, it is first
+ removed from that window.
+*/
+
+void Q3MainWindow::addDockWindow(Q3DockWindow *dockWindow,
+ Qt::Dock edge, bool newLine)
+{
+ Q_D(Q3MainWindow);
+#if defined (Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
+ extern WindowPtr qt_mac_window_for(const QWidget*); //qwidget_mac.cpp
+ if(isWindow() && edge == Qt::DockTop) {
+ d->createWinId();
+ ChangeWindowAttributes(qt_mac_window_for(this), kWindowToolbarButtonAttribute, 0);
+ }
+#endif
+ moveDockWindow(dockWindow, edge);
+ dockWindow->setNewLine(newLine);
+ if (!d->dockWindows.contains(dockWindow)) {
+ d->dockWindows.append(dockWindow);
+ connect(dockWindow, SIGNAL(placeChanged(Q3DockWindow::Place)),
+ this, SLOT(slotPlaceChanged()));
+ dockWindow->installEventFilter(this);
+ }
+ dockWindow->setOpaqueMoving(d->opaque);
+}
+
+
+/*!
+ \overload
+
+ Adds \a dockWindow to the dock area with label \a label.
+
+ If \a newLine is false (the default) the \a dockWindow is added at
+ the end of the \a edge. For vertical edges the end is at the
+ bottom, for horizontal edges (including \c Minimized) the end is
+ at the right. If \a newLine is true a new line of dock windows is
+ started with \a dockWindow as the first (left-most and top-most)
+ dock window.
+
+ If \a dockWindow is managed by another main window, it is first
+ removed from that window.
+*/
+
+void Q3MainWindow::addDockWindow(Q3DockWindow * dockWindow, const QString &label,
+ Qt::Dock edge, bool newLine)
+{
+ addDockWindow(dockWindow, edge, newLine);
+#ifndef QT_NO_TOOLBAR
+ Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(dockWindow);
+ if (tb)
+ tb->setLabel(label);
+#endif
+}
+
+/*!
+ Moves \a dockWindow to the end of the \a edge.
+
+ For vertical edges the end is at the bottom, for horizontal edges
+ (including \c Minimized) the end is at the right.
+
+ If \a dockWindow is managed by another main window, it is first
+ removed from that window.
+*/
+
+void Q3MainWindow::moveDockWindow(Q3DockWindow * dockWindow, Qt::Dock edge)
+{
+ Q_D(Q3MainWindow);
+ Qt::Orientation oo = dockWindow->orientation();
+ switch (edge) {
+ case Qt::DockTop:
+ if (dockWindow->area() != d->topDock)
+ dockWindow->removeFromDock(false);
+ d->topDock->moveDockWindow(dockWindow);
+ emit dockWindowPositionChanged(dockWindow);
+ break;
+ case Qt::DockBottom:
+ if (dockWindow->area() != d->bottomDock)
+ dockWindow->removeFromDock(false);
+ d->bottomDock->moveDockWindow(dockWindow);
+ emit dockWindowPositionChanged(dockWindow);
+ break;
+ case Qt::DockRight:
+ if (dockWindow->area() != d->rightDock)
+ dockWindow->removeFromDock(false);
+ d->rightDock->moveDockWindow(dockWindow);
+ emit dockWindowPositionChanged(dockWindow);
+ break;
+ case Qt::DockLeft:
+ if (dockWindow->area() != d->leftDock)
+ dockWindow->removeFromDock(false);
+ d->leftDock->moveDockWindow(dockWindow);
+ emit dockWindowPositionChanged(dockWindow);
+ break;
+ case Qt::DockTornOff:
+ dockWindow->undock();
+ break;
+ case Qt::DockMinimized:
+ dockWindow->undock(d->hideDock);
+ break;
+ case Qt::DockUnmanaged:
+ break;
+ }
+
+ if (oo != dockWindow->orientation())
+ dockWindow->setOrientation(dockWindow->orientation());
+}
+
+/*!
+ \overload
+
+ Moves \a dockWindow to position \a index within the \a edge dock
+ area.
+
+ Any dock windows with positions \a index or higher have their
+ position number incremented and any of these on the same line are
+ moved right (down for vertical dock areas) to make room.
+
+ If \a nl is true, a new dock window line is created below the line
+ in which the moved dock window appears and the moved dock window,
+ with any others with higher positions on the same line, is moved
+ to this new line.
+
+ The \a extraOffset is the space to put between the left side of
+ the dock area (top side for vertical dock areas) and the dock
+ window. (This is mostly used for restoring dock windows to the
+ positions the user has dragged them to.)
+
+ If \a dockWindow is managed by another main window, it is first
+ removed from that window.
+*/
+
+void Q3MainWindow::moveDockWindow(Q3DockWindow * dockWindow, Qt::Dock edge, bool nl, int index, int extraOffset)
+{
+ Q_D(Q3MainWindow);
+ Qt::Orientation oo = dockWindow->orientation();
+
+ dockWindow->setNewLine(nl);
+ dockWindow->setOffset(extraOffset);
+ switch (edge) {
+ case Qt::DockTop:
+ if (dockWindow->area() != d->topDock)
+ dockWindow->removeFromDock(false);
+ d->topDock->moveDockWindow(dockWindow, index);
+ break;
+ case Qt::DockBottom:
+ if (dockWindow->area() != d->bottomDock)
+ dockWindow->removeFromDock(false);
+ d->bottomDock->moveDockWindow(dockWindow, index);
+ break;
+ case Qt::DockRight:
+ if (dockWindow->area() != d->rightDock)
+ dockWindow->removeFromDock(false);
+ d->rightDock->moveDockWindow(dockWindow, index);
+ break;
+ case Qt::DockLeft:
+ if (dockWindow->area() != d->leftDock)
+ dockWindow->removeFromDock(false);
+ d->leftDock->moveDockWindow(dockWindow, index);
+ break;
+ case Qt::DockTornOff:
+ dockWindow->undock();
+ break;
+ case Qt::DockMinimized:
+ dockWindow->undock(d->hideDock);
+ break;
+ case Qt::DockUnmanaged:
+ break;
+ }
+
+ if (oo != dockWindow->orientation())
+ dockWindow->setOrientation(dockWindow->orientation());
+}
+
+/*!
+ Removes \a dockWindow from the main window's docking area,
+ provided \a dockWindow is non-null and managed by this main
+ window.
+*/
+
+void Q3MainWindow::removeDockWindow(Q3DockWindow * dockWindow)
+{
+ Q_D(Q3MainWindow);
+
+#if defined (Q_WS_MAC) && !defined (QT_MAC_USE_COCOA)
+ extern WindowPtr qt_mac_window_for(const QWidget*); //qwidget_mac.cpp
+ if(isWindow() && dockWindow->area() == topDock() && !dockWindows(Qt::DockTop).count())
+ ChangeWindowAttributes(qt_mac_window_for(this), 0, kWindowToolbarButtonAttribute);
+#endif
+
+ dockWindow->hide();
+ d->dockWindows.removeAll(dockWindow);
+ disconnect(dockWindow, SIGNAL(placeChanged(Q3DockWindow::Place)),
+ this, SLOT(slotPlaceChanged()));
+ dockWindow->removeEventFilter(this);
+}
+
+/*!
+ Sets up the geometry management of the window. It is called
+ automatically when needed, so you shouldn't need to call it.
+*/
+
+void Q3MainWindow::setUpLayout()
+{
+ Q_D(Q3MainWindow);
+#ifndef QT_NO_MENUBAR
+ if (!d->mb) {
+ // slightly evil hack here. reconsider this
+ QObjectList l = queryList("QMenuBar", 0, false, false);
+ if (l.size())
+ d->mb = menuBar();
+ }
+#endif
+ if (!d->sb) {
+ // as above.
+ QObjectList l = queryList("QStatusBar", 0, false, false);
+ if (l.size())
+ d->sb = statusBar();
+ }
+
+ if (!d->tll) {
+ d->tll = new QBoxLayout(this, QBoxLayout::Down);
+ d->tll->setResizeMode(minimumSize().isNull() ? QLayout::Minimum : QLayout::FreeResize);
+ d->mwl = new Q3MainWindowLayout(this);
+ } else {
+ d->tll->setMenuBar(0);
+ QLayoutItem *item;
+ while ((item = d->tll->takeAt(0))) {
+ if (item != d->mwl)
+ delete item;
+ }
+ }
+
+#ifndef QT_NO_MENUBAR
+ if (d->mb && d->mb->isVisibleTo(this)) {
+ d->tll->setMenuBar(d->mb);
+ if (style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, 0, this))
+ d->tll->addSpacing(d->movable ? 1 : 2);
+ }
+#endif
+
+ d->tll->addWidget(d->hideDock);
+ if(d->topDock->parentWidget() == this)
+ d->tll->addWidget(d->topDock);
+
+ Q3MainWindowLayout *mwl = d->mwl;
+ d->tll->addItem(mwl);
+ d->tll->setStretchFactor(mwl, 1);
+
+ if(d->leftDock->parentWidget() == this)
+ mwl->setLeftDock(d->leftDock);
+ if (centralWidget())
+ mwl->setCentralWidget(centralWidget());
+ if(d->rightDock->parentWidget() == this)
+ mwl->setRightDock(d->rightDock);
+
+ if(d->bottomDock->parentWidget() == this)
+ d->tll->addWidget(d->bottomDock);
+
+ if (d->sb && d->sb->parentWidget() == this) {
+ d->tll->addWidget(d->sb, 0);
+ // make the sb stay on top of tool bars if there isn't enough space
+ d->sb->raise();
+ }
+}
+
+/*! \reimp */
+void Q3MainWindow::setVisible(bool visible)
+{
+ Q_D(Q3MainWindow);
+ if (visible) {
+ if (!d->tll)
+ setUpLayout();
+
+ // show all floating dock windows not explicitly hidden
+ if (!isVisible()) {
+ for (int i = 0; i < d->dockWindows.size(); ++i) {
+ Q3DockWindow *dw = d->dockWindows.at(i);
+ if (dw->isWindow() && !dw->isVisible() && !dw->testAttribute(Qt::WA_WState_Hidden)) {
+ reinterpret_cast<Q3MainWindow *>(dw)->setAttribute(Qt::WA_WState_Hidden);
+ dw->show();
+ }
+ }
+ }
+ } else if (isVisible()) {
+ for (int i = 0; i < d->dockWindows.size(); ++i) {
+ Q3DockWindow *dw = d->dockWindows.at(i);
+ if (dw->isWindow() && dw->isVisible()) {
+ dw->hide(); // implicit hide, so clear forcehide
+ reinterpret_cast<Q3MainWindow *>(dw)->setAttribute(Qt::WA_WState_Hidden, false);
+ }
+ }
+ }
+ QWidget::setVisible(visible);
+}
+
+
+/*! \reimp */
+QSize Q3MainWindow::sizeHint() const
+{
+ Q3MainWindow* that = (Q3MainWindow*) this;
+ // Workaround: because d->tll get's deleted in
+ // totalSizeHint->polish->sendPostedEvents->childEvent->triggerLayout
+ QApplication::sendPostedEvents(that, QEvent::ChildInserted);
+ if (!that->d_func()->tll)
+ that->setUpLayout();
+ return that->d_func()->tll->totalSizeHint();
+}
+
+/*! \reimp */
+QSize Q3MainWindow::minimumSizeHint() const
+{
+ Q_D(const Q3MainWindow);
+ if (!d->tll) {
+ Q3MainWindow* that = (Q3MainWindow*) this;
+ that->setUpLayout();
+ }
+ return d->tll->totalMinimumSize();
+}
+
+/*!
+ Sets the central widget for this main window to \a w.
+
+ The central widget is surrounded by the left, top, right and
+ bottom dock areas. The menu bar is above the top dock area.
+
+ \sa centralWidget()
+*/
+
+void Q3MainWindow::setCentralWidget(QWidget * w)
+{
+ Q_D(Q3MainWindow);
+ if (d->mc)
+ d->mc->removeEventFilter(this);
+ d->mc = w;
+ if (d->mc)
+ d->mc->installEventFilter(this);
+ triggerLayout();
+}
+
+
+/*!
+ Returns a pointer to the main window's central widget.
+
+ The central widget is surrounded by the left, top, right and
+ bottom dock areas. The menu bar is above the top dock area.
+
+ \sa setCentralWidget()
+*/
+
+QWidget * Q3MainWindow::centralWidget() const
+{
+ return d_func()->mc;
+}
+
+
+/*! \reimp */
+
+void Q3MainWindow::paintEvent(QPaintEvent *)
+{
+ Q_D(Q3MainWindow);
+ if (d->mb &&
+ style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, 0, this)) {
+ QPainter p(this);
+ int y = d->mb->height() + 1;
+ QStyleOption opt(0, QStyleOption::SO_Default);
+ opt.rect.setRect(0, y, width(), 1);
+ opt.palette = palette();
+ opt.state = QStyle::State_Sunken;
+ style()->drawPrimitive(QStyle::PE_Q3Separator, &opt, &p, this);
+ }
+}
+
+
+bool Q3MainWindow::dockMainWindow(QObject *dock) const
+{
+ while (dock) {
+ if (dock->parent() &&
+ dock->parent() == const_cast<Q3MainWindow*>(this))
+ return true;
+ if (qobject_cast<Q3MainWindow*>(dock->parent()))
+ return false;
+ dock = dock->parent();
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3MainWindow::eventFilter(QObject* o, QEvent *e)
+{
+ Q_D(Q3MainWindow);
+ if (e->type() == QEvent::Show && o == this) {
+ if (!d->tll)
+ setUpLayout();
+ d->tll->activate();
+ } else if (e->type() == QEvent::ContextMenu && d->dockMenu &&
+ ((qobject_cast<Q3DockArea*>(o) && dockMainWindow(o)) || o == d->hideDock || o == d->mb)) {
+ if (showDockMenu(((QMouseEvent*)e)->globalPos())) {
+ ((QContextMenuEvent*)e)->accept();
+ return true;
+ }
+ }
+
+ return QWidget::eventFilter(o, e);
+}
+
+
+/*!
+ Monitors events, received in \a e, to ensure the layout is updated.
+*/
+void Q3MainWindow::childEvent(QChildEvent* e)
+{
+ Q_D(Q3MainWindow);
+ if (e->type() == QEvent::ChildRemoved) {
+ if (e->child() == 0 ||
+ !e->child()->isWidgetType() ||
+ ((QWidget*)e->child())->isWindow()) {
+ // nothing
+ } else if (e->child() == d->sb) {
+ d->sb = 0;
+ triggerLayout();
+ } else if (e->child() == d->mb) {
+ d->mb = 0;
+ triggerLayout();
+ } else if (e->child() == d->mc) {
+ d->mc = 0;
+ d->mwl->setCentralWidget(0);
+ triggerLayout();
+ } else if (qobject_cast<Q3DockWindow*>(e->child())) {
+ removeDockWindow((Q3DockWindow *)(e->child()));
+ d->appropriate.remove((Q3DockWindow*)e->child());
+ triggerLayout();
+ }
+ } else if (e->type() == QEvent::ChildInserted && !d->sb) {
+ d->sb = qobject_cast<QStatusBar*>(e->child());
+ if (d->sb) {
+ if (d->tll) {
+ if (!d->tll->findWidget(d->sb))
+ d->tll->addWidget(d->sb);
+ } else {
+ triggerLayout();
+ }
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3MainWindow::event(QEvent * e)
+{
+ Q_D(Q3MainWindow);
+#ifndef QT_NO_STATUSTIP
+ if (e->type() == QEvent::StatusTip) {
+ if (d->sb) {
+ d->sb->showMessage(static_cast<QStatusTipEvent*>(e)->tip());
+ return true;
+ }
+ }
+#endif
+ if (e->type() == QEvent::ToolBarChange) {
+ // Keep compatibility with the Qt 3 main window, use the real main window
+ // or reimplement if you want proper handling.
+ int deltaH = 0;
+ Q3DockArea *area = topDock();
+ if (area->width() >= area->height()) {
+ deltaH = area->sizeHint().height();
+ if (!area->isVisible()) {
+ area->show();
+ } else {
+ area->hide();
+ deltaH = -deltaH;
+ }
+ }
+
+ if (deltaH) {
+ QApplication::sendPostedEvents(this, QEvent::LayoutRequest);
+ resize(width(), height() + deltaH);
+ }
+ return true;
+ }
+ if (e->type() == QEvent::ChildRemoved && ((QChildEvent*)e)->child() == d->mc) {
+ d->mc->removeEventFilter(this);
+ d->mc = 0;
+ d->mwl->setCentralWidget(0);
+ }
+
+ if (e->type() == QEvent::MenubarUpdated) {
+ QMenubarUpdatedEvent * const event = static_cast<QMenubarUpdatedEvent *>(e);
+ if (event->menuBar() && event->menuBar()->parent() == this) {
+ triggerLayout();
+ update();
+ }
+ }
+ return QWidget::event(e);
+}
+
+
+/*!
+ \property Q3MainWindow::usesBigPixmaps
+ \brief whether big pixmaps are enabled
+
+ If false (the default), the tool buttons will use small pixmaps;
+ otherwise big pixmaps will be used.
+
+ Tool buttons and other widgets that wish to respond to this
+ setting are responsible for reading the correct state on startup,
+ and for connecting to the main window's widget's
+ pixmapSizeChanged() signal.
+*/
+
+bool Q3MainWindow::usesBigPixmaps() const
+{
+ return d_func()->ubp;
+}
+
+void Q3MainWindow::setUsesBigPixmaps(bool enable)
+{
+ Q_D(Q3MainWindow);
+ if (enable == (bool)d->ubp)
+ return;
+
+ d->ubp = enable;
+ emit pixmapSizeChanged(enable);
+
+ QObjectList l = queryList("QLayout");
+ for (int i = 0; i < l.size(); ++i)
+ static_cast<QLayout *>(l.at(i))->activate();
+}
+
+/*!
+ \property Q3MainWindow::usesTextLabel
+ \brief whether text labels for toolbar buttons are enabled
+
+ If disabled (the default), the tool buttons will not use text
+ labels. If enabled, text labels will be used.
+
+ Tool buttons and other widgets that wish to respond to this
+ setting are responsible for reading the correct state on startup,
+ and for connecting to the main window's widget's
+ usesTextLabelChanged() signal.
+
+ \sa QToolButton::setUsesTextLabel()
+*/
+
+bool Q3MainWindow::usesTextLabel() const
+{
+ return d_func()->utl;
+}
+
+
+void Q3MainWindow::setUsesTextLabel(bool enable)
+{
+ Q_D(Q3MainWindow);
+ if (enable == (bool)d->utl)
+ return;
+
+ d->utl = enable;
+ emit usesTextLabelChanged(enable);
+
+ QObjectList l = queryList("QLayout");
+ for (int i = 0; i < l.size(); ++i)
+ static_cast<QLayout *>(l.at(i))->activate();
+ triggerLayout(false);
+}
+
+
+/*!
+ \fn void Q3MainWindow::pixmapSizeChanged(bool b)
+
+ This signal is emitted whenever the setUsesBigPixmaps() is called
+ with a value different to the current setting. The new value is
+ passed in \a b. All widgets that should respond to such changes,
+ e.g. toolbar buttons, must connect to this signal.
+*/
+
+/*!
+ \fn void Q3MainWindow::usesTextLabelChanged(bool b)
+
+ This signal is emitted whenever the setUsesTextLabel() is called
+ with a value different to the current setting. The new value is
+ passed in \a b. All widgets that should respond to such changes,
+ e.g. toolbar buttons, must connect to this signal.
+*/
+
+/*!
+ \fn void Q3MainWindow::dockWindowPositionChanged(Q3DockWindow *dockWindow)
+
+ This signal is emitted when the \a dockWindow has changed its
+ position. A change in position occurs when a dock window is moved
+ within its dock area or moved to another dock area (including the
+ \c Minimized and \c TearOff dock areas).
+
+ \sa getLocation()
+*/
+
+void Q3MainWindow::setRightJustification(bool enable)
+{
+ Q_D(Q3MainWindow);
+ if (enable == (bool)d->justify)
+ return;
+ d->justify = enable;
+ triggerLayout(true);
+}
+
+
+/*!
+ \property Q3MainWindow::rightJustification
+ \brief whether the main window right-justifies its dock windows
+
+ If disabled (the default), stretchable dock windows are expanded,
+ and non-stretchable dock windows are given the minimum space they
+ need. Since most dock windows are not stretchable, this usually
+ results in an unjustified right edge (or unjustified bottom edge
+ for a vertical dock area). If enabled, the main window will
+ right-justify its dock windows.
+
+ \sa Q3DockWindow::setVerticalStretchable(), Q3DockWindow::setHorizontalStretchable()
+*/
+
+bool Q3MainWindow::rightJustification() const
+{
+ return d_func()->justify;
+}
+
+/*! \internal
+ */
+
+void Q3MainWindow::triggerLayout(bool deleteLayout)
+{
+ Q_D(Q3MainWindow);
+ if (deleteLayout || !d->tll)
+ setUpLayout();
+ QApplication::postEvent(this, new QEvent(QEvent::LayoutHint));
+}
+
+/*!
+ Enters 'What's This?' mode and returns immediately.
+
+ This is the same as QWhatsThis::enterWhatsThisMode(), but
+ implemented as a main window object's slot. This way it can easily
+ be used for popup menus, for example:
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 4
+
+ \sa Q3WhatsThis::enterWhatsThisMode()
+*/
+void Q3MainWindow::whatsThis()
+{
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::enterWhatsThisMode();
+#endif
+}
+
+/*!
+ Finds the location of the dock window \a dw.
+
+ If the \a dw dock window is found in the main window the function
+ returns true and populates the \a dock variable with the dw's dock
+ area and the \a index with the dw's position within the dock area.
+ It also sets \a nl to true if the \a dw begins a new line
+ (otherwise false), and \a extraOffset with the dock window's offset.
+
+ If the \a dw dock window is not found then the function returns
+ false and the state of \a dock, \a index, \a nl and \a extraOffset
+ is undefined.
+
+ If you want to save and restore dock window positions then use
+ operator>>() and operator<<().
+*/
+
+bool Q3MainWindow::getLocation(Q3DockWindow *dw, Qt::Dock &dock, int &index, bool &nl, int &extraOffset) const
+{
+ Q_D(const Q3MainWindow);
+ dock = Qt::DockTornOff;
+ if (d->topDock->hasDockWindow(dw, &index))
+ dock = Qt::DockTop;
+ else if (d->bottomDock->hasDockWindow(dw, &index))
+ dock = Qt::DockBottom;
+ else if (d->leftDock->hasDockWindow(dw, &index))
+ dock = Qt::DockLeft;
+ else if (d->rightDock->hasDockWindow(dw, &index))
+ dock = Qt::DockRight;
+ else if (dw->parentWidget() == d->hideDock) {
+ index = 0;
+ dock = Qt::DockMinimized;
+ } else {
+ index = 0;
+ }
+ nl = dw->newLine();
+ extraOffset = dw->offset();
+ return true;
+}
+
+#ifndef QT_NO_TOOLBAR
+/*!
+ Returns a list of all the toolbars which are in the \a dock dock
+ area, regardless of their state.
+
+ For example, the \c TornOff dock area may contain closed toolbars
+ but these are returned along with the visible toolbars.
+
+ \sa dockWindows()
+*/
+
+QList<Q3ToolBar *> Q3MainWindow::toolBars(Qt::Dock dock) const
+{
+ QList<Q3DockWindow *> lst = dockWindows(dock);
+ QList<Q3ToolBar *> tbl;
+ for (int i = 0; i < lst.size(); ++i) {
+ Q3ToolBar *tb = qobject_cast<Q3ToolBar *>(lst.at(i));
+ if (tb)
+ tbl.append(tb);
+ }
+ return tbl;
+}
+#endif
+
+/*!
+ Returns a list of all the dock windows which are in the \a dock
+ dock area, regardless of their state.
+
+ For example, the Qt::DockTornOff dock area may contain closed dock
+ windows but these are returned along with the visible dock
+ windows.
+*/
+
+QList<Q3DockWindow *> Q3MainWindow::dockWindows(Qt::Dock dock) const
+{
+ Q_D(const Q3MainWindow);
+ QList<Q3DockWindow *> lst;
+ switch (dock) {
+ case Qt::DockTop:
+ return d->topDock->dockWindowList();
+ case Qt::DockBottom:
+ return d->bottomDock->dockWindowList();
+ case Qt::DockLeft:
+ return d->leftDock->dockWindowList();
+ case Qt::DockRight:
+ return d->rightDock->dockWindowList();
+ case Qt::DockTornOff: {
+ for (int i = 0; i < d->dockWindows.size(); ++i) {
+ Q3DockWindow *w = d->dockWindows.at(i);
+ if (!w->area() && w->place() == Q3DockWindow::OutsideDock)
+ lst.append(w);
+ }
+ }
+ return lst;
+ case Qt::DockMinimized: {
+ QObjectList childList = d->hideDock->children();
+ for (int i = 0; i < childList.size(); ++i) {
+ Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(childList.at(i));
+ if (dw)
+ lst.append(dw);
+ }
+ }
+ return lst;
+ default:
+ break;
+ }
+ return lst;
+}
+
+/*!
+ \overload
+
+ Returns the list of dock windows which belong to this main window,
+ regardless of which dock area they are in or what their state is,
+ (e.g. irrespective of whether they are visible or not).
+*/
+
+QList<Q3DockWindow *> Q3MainWindow::dockWindows() const
+{
+ return d_func()->dockWindows;
+}
+
+void Q3MainWindow::setDockWindowsMovable(bool enable)
+{
+ Q_D(Q3MainWindow);
+ d->movable = enable;
+ QObjectList l = queryList("Q3DockWindow");
+ for (int i = 0; i < l.size(); ++i)
+ static_cast<Q3DockWindow*>(l.at(i))->setMovingEnabled(enable);
+}
+
+/*!
+ \property Q3MainWindow::dockWindowsMovable
+ \brief whether the dock windows are movable
+
+ If true (the default), the user will be able to move movable dock
+ windows from one Q3MainWindow dock area to another, including the
+ \c TearOff area (i.e. where the dock window floats freely as a
+ window in its own right), and the \c Minimized area (where only
+ the dock window's handle is shown below the menu bar). Movable
+ dock windows can also be moved within Q3MainWindow dock areas, i.e.
+ to rearrange them within a dock area.
+
+ If false the user will not be able to move any dock windows.
+
+ By default dock windows are moved transparently (i.e. only an
+ outline rectangle is shown during the drag), but this setting can
+ be changed with setOpaqueMoving().
+
+ \sa setDockEnabled(), setOpaqueMoving()
+*/
+
+bool Q3MainWindow::dockWindowsMovable() const
+{
+ return d_func()->movable;
+}
+
+void Q3MainWindow::setOpaqueMoving(bool b)
+{
+ Q_D(Q3MainWindow);
+ d->opaque = b;
+ QObjectList l = queryList("Q3DockWindow");
+ for (int i = 0; i < l.size(); ++i)
+ static_cast<Q3DockWindow*>(l.at(i))->setOpaqueMoving(b);
+}
+
+/*!
+ \property Q3MainWindow::opaqueMoving
+ \brief whether dock windows are moved opaquely
+
+ If true the dock windows of the main window are shown opaquely
+ (i.e. it shows the toolbar as it looks when docked) whilst it is
+ being moved. If false (the default) they are shown transparently,
+ (i.e. as an outline rectangle).
+
+ \warning Opaque moving of toolbars and dockwindows is known to
+ have several problems. We recommend avoiding the use of this
+ feature for the time being. We intend fixing the problems in a
+ future release.
+*/
+
+bool Q3MainWindow::opaqueMoving() const
+{
+ return d_func()->opaque;
+}
+
+/*!
+ This function will line up dock windows within the visible dock
+ areas (\c Top, \c Left, \c Right and \c Bottom) as compactly as
+ possible.
+
+ If \a keepNewLines is true, all dock windows stay on their
+ original lines. If \a keepNewLines is false then newlines may be
+ removed to achieve the most compact layout possible.
+
+ The method only works if dockWindowsMovable() returns true.
+*/
+
+void Q3MainWindow::lineUpDockWindows(bool keepNewLines)
+{
+ Q_D(const Q3MainWindow);
+ if (!dockWindowsMovable())
+ return;
+ d->topDock->lineUp(keepNewLines);
+ d->leftDock->lineUp(keepNewLines);
+ d->rightDock->lineUp(keepNewLines);
+ d->bottomDock->lineUp(keepNewLines);
+}
+
+/*!
+ Returns true, if the dock window menu is enabled; otherwise
+ returns false.
+
+ The menu lists the (appropriate()) dock windows (which may be
+ shown or hidden), and has a "Line Up Dock Windows" menu item. It
+ will also have a "Customize" menu item if isCustomizable() returns
+ true.
+
+ \sa setDockEnabled(), lineUpDockWindows() appropriate()
+ setAppropriate()
+*/
+
+bool Q3MainWindow::isDockMenuEnabled() const
+{
+ return d_func()->dockMenu;
+}
+
+/*!
+ If \a b is true, then right clicking on a dock window or dock area
+ will pop up the dock window menu. If \a b is false, right clicking
+ a dock window or dock area will not pop up the menu.
+
+ The menu lists the (appropriate()) dock windows (which may be
+ shown or hidden), and has a "Line Up Dock Windows" item. It will
+ also have a "Customize" menu item if isCustomizable() returns
+ true.
+
+ \sa lineUpDockWindows(), isDockMenuEnabled()
+*/
+
+void Q3MainWindow::setDockMenuEnabled(bool b)
+{
+ d_func()->dockMenu = b;
+}
+
+/*!
+ Creates the dock window menu which contains all toolbars (if \a
+ dockWindows is \c OnlyToolBars), all dock windows (if \a
+ dockWindows is \c NoToolBars) or all toolbars and dock windows (if
+ \a dockWindows is \c AllDockWindows - the default).
+
+ This function is called internally when necessary, e.g. when the
+ user right clicks a dock area (providing isDockMenuEnabled()
+ returns true). You can reimplement this function if you wish to
+ customize the behavior.
+
+ The menu items representing the toolbars and dock windows are
+ checkable. The visible dock windows are checked and the hidden
+ dock windows are unchecked. The user can click a menu item to
+ change its state (show or hide the dock window).
+
+ The list and the state are always kept up-to-date.
+
+ Toolbars and dock windows which are not appropriate in the current
+ context (see setAppropriate()) are not listed in the menu.
+
+ The menu also has a menu item for lining up the dock windows.
+
+ If isCustomizable() returns true, a Customize menu item is added
+ to the menu, which if clicked will call customize(). The
+ isCustomizable() function we provide returns false and customize()
+ does nothing, so they must be reimplemented in a subclass to be
+ useful.
+*/
+
+Q3PopupMenu *Q3MainWindow::createDockWindowMenu(DockWindows dockWindows) const
+{
+ Q_D(const Q3MainWindow);
+ QObjectList l = queryList("Q3DockWindow");
+ if (l.isEmpty())
+ return 0;
+
+ Q3PopupMenu *menu = new Q3PopupMenu((Q3MainWindow*)this);
+ menu->setObjectName(QLatin1String("qt_customize_menu"));
+ d->dockWindowModes.replace( menu, dockWindows );
+ menu->setCheckable(true);
+ connect( menu, SIGNAL(aboutToShow()), this, SLOT(menuAboutToShow()) );
+ return menu;
+}
+
+/*!
+ This slot is called from the aboutToShow() signal of the default
+ dock menu of the mainwindow. The default implementation
+ initializes the menu with all dock windows and toolbars in this
+ slot.
+*/
+
+void Q3MainWindow::menuAboutToShow()
+{
+ Q_D(Q3MainWindow);
+ Q3PopupMenu *menu = (Q3PopupMenu*)sender();
+ menu->clear();
+
+ DockWindows dockWindows;
+ {
+ QMap<Q3PopupMenu*, DockWindows>::Iterator it = d->dockWindowModes.find( menu );
+ if ( it == d->dockWindowModes.end() )
+ return;
+ dockWindows = (*it);
+ }
+
+ QObjectList l = queryList("Q3DockWindow");
+ bool empty = true;
+ if (!l.isEmpty()) {
+ if (dockWindows == AllDockWindows || dockWindows == NoToolBars) {
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = (Q3DockWindow*)l.at(i);
+ if (!appropriate(dw) || qobject_cast<Q3ToolBar*>(dw) || !dockMainWindow(dw))
+ continue;
+ QString label = dw->windowTitle();
+ if (!label.isEmpty()) {
+ QAction *act = menu->addAction(label);
+ act->setCheckable(true);
+ act->setChecked(dw->isVisible());
+ QObject::connect(act, SIGNAL(triggered()), dw, SLOT(toggleVisible()));
+ empty = false;
+ }
+ }
+ }
+ if (!empty) {
+ menu->addSeparator();
+ empty = true;
+ }
+#ifndef QT_NO_TOOLBAR
+ if (dockWindows == AllDockWindows || dockWindows == OnlyToolBars) {
+ for (int i = 0; i < l.size(); ++i) {
+ Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(l.at(i));
+ if (!tb || !appropriate(tb) || !dockMainWindow(tb))
+ continue;
+ QString label = tb->label();
+ if (!label.isEmpty()) {
+ QAction *act = menu->addAction(label);
+ act->setCheckable(true);
+ act->setChecked(tb->isVisible());
+ QObject::connect(act, SIGNAL(triggered()), tb, SLOT(toggleVisible()));
+ empty = false;
+ }
+ }
+ }
+#endif
+ }
+ if (!empty) {
+ menu->addSeparator();
+ empty = true;
+ }
+
+ if (dockWindowsMovable())
+ menu->addAction(tr("Line up"), this, SLOT(doLineUp()));
+ if (isCustomizable())
+ menu->addAction(tr("Customize..."), this, SLOT(customize()));
+}
+
+
+/*!
+ Shows the dock menu at the position \a globalPos. The menu lists
+ the dock windows so that they can be shown (or hidden), lined up,
+ and possibly customized. Returns true if the menu is shown;
+ otherwise returns false.
+
+ If you want a custom menu, reimplement this function. You can
+ create the menu from scratch or call createDockWindowMenu() and
+ modify the result.
+
+ The default implementation uses the dock window menu which gets
+ created by createDockWindowMenu(). You can reimplement
+ createDockWindowMenu() if you want to use your own specialized
+ popup menu.
+*/
+
+bool Q3MainWindow::showDockMenu(const QPoint &globalPos)
+{
+ Q_D(Q3MainWindow);
+ if (!d->dockMenu)
+ return false;
+
+ if(Q3PopupMenu *ret = createDockWindowMenu()) {
+ ret->exec(globalPos);
+ delete ret;
+ return true;
+ }
+ return false;
+}
+
+void Q3MainWindow::slotPlaceChanged()
+{
+ QObject* obj = (QObject*)sender();
+ Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(obj);
+ if (dw)
+ emit dockWindowPositionChanged(dw);
+#ifndef QT_NO_TOOLBAR
+ Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(obj);
+ if (tb)
+ emit toolBarPositionChanged(tb);
+#endif
+}
+
+/*!
+ \internal
+ For internal use of Q3DockWindow only.
+ */
+
+Q3DockArea *Q3MainWindow::dockingArea(const QPoint &p)
+{
+ Q_D(Q3MainWindow);
+ int mh = d->mb ? d->mb->height() : 0;
+ int sh = d->sb ? d->sb->height() : 0;
+ if (p.x() >= -5 && p.x() <= 100 && p.y() > mh && p.y() - height() - sh)
+ return d->leftDock;
+ if (p.x() >= width() - 100 && p.x() <= width() + 5 && p.y() > mh && p.y() - height() - sh)
+ return d->rightDock;
+ if (p.y() >= -5 && p.y() < mh + 100 && p.x() >= 0 && p.x() <= width())
+ return d->topDock;
+ if (p.y() >= height() - sh - 100 && p.y() <= height() + 5 && p.x() >= 0 && p.x() <= width())
+ return d->bottomDock;
+ return 0;
+}
+
+/*!
+ Returns true if \a dw is a dock window known to the main window;
+ otherwise returns false.
+*/
+
+bool Q3MainWindow::hasDockWindow(Q3DockWindow *dw)
+{
+ return d_func()->dockWindows.contains(dw);
+}
+
+/*!
+ Returns the \c Left dock area
+
+ \sa rightDock() topDock() bottomDock()
+*/
+
+Q3DockArea *Q3MainWindow::leftDock() const
+{
+ return d_func()->leftDock;
+}
+
+/*!
+ Returns the \c Right dock area
+
+ \sa leftDock() topDock() bottomDock()
+*/
+
+Q3DockArea *Q3MainWindow::rightDock() const
+{
+ return d_func()->rightDock;
+}
+
+/*!
+ Returns the \c Top dock area
+
+ \sa bottomDock() leftDock() rightDock()
+*/
+
+Q3DockArea *Q3MainWindow::topDock() const
+{
+ return d_func()->topDock;
+}
+
+/*!
+ Returns a pointer the \c Bottom dock area
+
+ \sa topDock() leftDock() rightDock()
+*/
+
+Q3DockArea *Q3MainWindow::bottomDock() const
+{
+ return d_func()->bottomDock;
+}
+
+/*!
+ This function is called when the user clicks the Customize menu
+ item on the dock window menu.
+
+ The customize menu item will only appear if isCustomizable()
+ returns true (it returns false by default).
+
+ The function is intended, for example, to provide the user with a
+ means of telling the application that they wish to customize the
+ main window, dock windows or dock areas.
+
+ The default implementation does nothing and the Customize menu
+ item is not shown on the right-click menu by default. If you want
+ the item to appear then reimplement isCustomizable() to return
+ true, and reimplement this function to do whatever you want.
+
+ \sa isCustomizable()
+*/
+
+void Q3MainWindow::customize()
+{
+}
+
+/*!
+ Returns true if the dock area dock window menu includes the
+ Customize menu item (which calls customize() when clicked).
+ Returns false by default, i.e. the popup menu will not contain a
+ Customize menu item. You will need to reimplement this function
+ and set it to return true if you wish the user to be able to see
+ the dock window menu.
+
+ \sa customize()
+*/
+
+bool Q3MainWindow::isCustomizable() const
+{
+ return false;
+}
+
+/*!
+ Returns true if it is appropriate to include a menu item for the
+ \a dw dock window in the dock window menu; otherwise returns
+ false.
+
+ The user is able to change the state (show or hide) a dock window
+ that has a menu item by clicking the item.
+
+ Call setAppropriate() to indicate whether or not a particular dock
+ window should appear on the popup menu.
+
+ \sa setAppropriate()
+*/
+
+bool Q3MainWindow::appropriate(Q3DockWindow *dw) const
+{
+ Q_D(const Q3MainWindow);
+ QMap<Q3DockWindow*, bool>::ConstIterator it = d->appropriate.find(dw);
+ if (it == d->appropriate.end())
+ return true;
+ return *it;
+}
+
+/*!
+ Use this function to control whether or not the \a dw dock
+ window's caption should appear as a menu item on the dock window
+ menu that lists the dock windows.
+
+ If \a a is true then the \a dw will appear as a menu item on the
+ dock window menu. The user is able to change the state (show or
+ hide) a dock window that has a menu item by clicking the item;
+ depending on the state of your application, this may or may not be
+ appropriate. If \a a is false the \a dw will not appear on the
+ popup menu.
+
+ \sa showDockMenu() isCustomizable() customize()
+*/
+
+void Q3MainWindow::setAppropriate(Q3DockWindow *dw, bool a)
+{
+ d_func()->appropriate.insert(dw, a);
+}
+
+#ifndef QT_NO_TEXTSTREAM
+static void saveDockArea(QTextStream &ts, Q3DockArea *a)
+{
+ QList<Q3DockWindow *> l = a->dockWindowList();
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = l.at(i);
+ ts << QString(dw->windowTitle());
+ ts << ',';
+ }
+ ts << endl;
+ ts << *a;
+}
+
+/*!
+ \relates Q3MainWindow
+
+ Writes the layout (sizes and positions) of the dock windows in the
+ dock areas of the Q3MainWindow \a mainWindow, including \c
+ Minimized and \c TornOff dock windows, to the text stream \a ts.
+
+ This can be used, for example, in conjunction with QSettings to
+ save the user's layout when the \a mainWindow receives a
+ close event.
+
+ \sa QWidget::closeEvent()
+*/
+
+QTextStream &operator<<(QTextStream &ts, const Q3MainWindow &mainWindow)
+{
+ QList<Q3DockWindow *> l = mainWindow.dockWindows(Qt::DockMinimized);
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = l.at(i);
+ ts << dw->windowTitle();
+ ts << ',';
+ }
+ ts << endl;
+
+ l = mainWindow.dockWindows(Qt::DockTornOff);
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = l.at(i);
+ ts << dw->windowTitle();
+ ts << ',';
+ }
+ ts << endl;
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = l.at(i);
+ ts << '[' << dw->windowTitle() << ','
+ << (int)dw->geometry().x() << ','
+ << (int)dw->geometry().y() << ','
+ << (int)dw->geometry().width() << ','
+ << (int)dw->geometry().height() << ','
+ << (int)dw->isVisible() << ']';
+ }
+ ts << endl;
+
+ saveDockArea(ts, mainWindow.topDock());
+ saveDockArea(ts, mainWindow.bottomDock());
+ saveDockArea(ts, mainWindow.rightDock());
+ saveDockArea(ts, mainWindow.leftDock());
+ return ts;
+}
+
+static void loadDockArea(const QStringList &names, Q3DockArea *a, Qt::Dock dl, QList<Q3DockWindow *> &l, Q3MainWindow *mw, QTextStream &ts)
+{
+ for (QStringList::ConstIterator it = names.begin(); it != names.end(); ++it) {
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = l.at(i);
+ if (dw->windowTitle() == *it) {
+ mw->addDockWindow(dw, dl);
+ break;
+ }
+ }
+ }
+ if (a) {
+ ts >> *a;
+ } else if (dl == Qt::DockTornOff) {
+ QString s = ts.readLine();
+ enum State { Pre, Name, X, Y, Width, Height, Visible, Post };
+ int state = Pre;
+ QString name, x, y, w, h, visible;
+ QChar c;
+ for (int i = 0; i < (int)s.length(); ++i) {
+ c = s[i];
+ if (state == Pre && c == QLatin1Char('[')) {
+ state++;
+ continue;
+ }
+ if (c == QLatin1Char(',') &&
+ (state == Name || state == X || state == Y || state == Width || state == Height)) {
+ state++;
+ continue;
+ }
+ if (state == Visible && c == QLatin1Char(']')) {
+ for (int i = 0; i < l.size(); ++i) {
+ Q3DockWindow *dw = l.at(i);
+ if (QString(dw->windowTitle()) == name) {
+ if (!qobject_cast<Q3ToolBar*>(dw))
+ dw->setGeometry(x.toInt(), y.toInt(), w.toInt(), h.toInt());
+ else
+ dw->setGeometry(x.toInt(), y.toInt(), dw->width(), dw->height());
+ if (!(bool)visible.toInt())
+ dw->hide();
+ else
+ dw->show();
+ break;
+ }
+ }
+
+ name = x = y = w = h = visible = QLatin1String("");
+
+ state = Pre;
+ continue;
+ }
+ if (state == Name)
+ name += c;
+ else if (state == X)
+ x += c;
+ else if (state == Y)
+ y += c;
+ else if (state == Width)
+ w += c;
+ else if (state == Height)
+ h += c;
+ else if (state == Visible)
+ visible += c;
+ }
+ }
+}
+
+/*!
+ \relates Q3MainWindow
+
+ Reads the layout (sizes and positions) of the dock windows in the
+ dock areas of the Q3MainWindow \a mainWindow from the text stream,
+ \a ts, including \c Minimized and \c TornOff dock windows.
+ Restores the dock windows and dock areas to these sizes and
+ positions. The layout information must be in the format produced
+ by operator<<().
+
+ This can be used, for example, in conjunction with QSettings to
+ restore the user's layout.
+*/
+
+QTextStream &operator>>(QTextStream &ts, Q3MainWindow &mainWindow)
+{
+ QList<Q3DockWindow *> l = mainWindow.dockWindows();
+
+ QString s = ts.readLine();
+ QStringList names = s.split(QLatin1Char(','));
+ loadDockArea(names, 0, Qt::DockMinimized, l, &mainWindow, ts);
+
+ s = ts.readLine();
+ names = s.split(QLatin1Char(','));
+ loadDockArea(names, 0, Qt::DockTornOff, l, &mainWindow, ts);
+
+ int i = 0;
+ Q3DockArea *areas[] = { mainWindow.topDock(), mainWindow.bottomDock(), mainWindow.rightDock(), mainWindow.leftDock() };
+ for (int dl = (int)Qt::DockTop; dl != (int)Qt::DockMinimized; ++dl, ++i) {
+ s = ts.readLine();
+ names = s.split(QLatin1Char(','));
+ loadDockArea(names, areas[i], (Qt::Dock)dl, l, &mainWindow, ts);
+ }
+ return ts;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "q3mainwindow.moc"
+
+#endif
diff --git a/src/qt3support/widgets/q3mainwindow.h b/src/qt3support/widgets/q3mainwindow.h
new file mode 100644
index 0000000..300be76
--- /dev/null
+++ b/src/qt3support/widgets/q3mainwindow.h
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3MAINWINDOW_H
+#define Q3MAINWINDOW_H
+
+#include <QtGui/qwidget.h>
+#include <Qt3Support/q3toolbar.h>
+#include <QtCore/qtextstream.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_MAINWINDOW
+
+class QMenuBar;
+class QStatusBar;
+class QToolTipGroup;
+class Q3MainWindowPrivate;
+class Q3MainWindowLayout;
+class Q3PopupMenu;
+class QHideDock;
+template<class T> class QList;
+
+class Q_COMPAT_EXPORT Q3MainWindow: public QWidget
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(Q3MainWindow)
+
+ Q_PROPERTY(bool rightJustification READ rightJustification WRITE setRightJustification DESIGNABLE false)
+ Q_PROPERTY(bool usesBigPixmaps READ usesBigPixmaps WRITE setUsesBigPixmaps)
+ Q_PROPERTY(bool usesTextLabel READ usesTextLabel WRITE setUsesTextLabel)
+ Q_PROPERTY(bool dockWindowsMovable READ dockWindowsMovable WRITE setDockWindowsMovable)
+ Q_PROPERTY(bool opaqueMoving READ opaqueMoving WRITE setOpaqueMoving)
+
+public:
+ Q3MainWindow(QWidget* parent=0, const char* name=0, Qt::WindowFlags f = Qt::WType_TopLevel);
+ ~Q3MainWindow();
+
+#ifndef QT_NO_MENUBAR
+ QMenuBar * menuBar() const;
+#endif
+ QStatusBar * statusBar() const;
+#if 0
+ QToolTipGroup * toolTipGroup() const;
+#endif
+
+ virtual void setCentralWidget(QWidget *);
+ QWidget * centralWidget() const;
+
+ virtual void setDockEnabled(Qt::Dock dock, bool enable);
+ bool isDockEnabled(Qt::Dock dock) const;
+ bool isDockEnabled(Q3DockArea *area) const;
+ virtual void setDockEnabled(Q3DockWindow *tb, Qt::Dock dock, bool enable);
+ bool isDockEnabled(Q3DockWindow *tb, Qt::Dock dock) const;
+ bool isDockEnabled(Q3DockWindow *tb, Q3DockArea *area) const;
+
+ virtual void addDockWindow(Q3DockWindow *, Qt::Dock = Qt::DockTop, bool newLine = false);
+ virtual void addDockWindow(Q3DockWindow *, const QString &label,
+ Qt::Dock = Qt::DockTop, bool newLine = false);
+ virtual void moveDockWindow(Q3DockWindow *, Qt::Dock = Qt::DockTop);
+ virtual void moveDockWindow(Q3DockWindow *, Qt::Dock, bool nl, int index, int extraOffset = -1);
+ virtual void removeDockWindow(Q3DockWindow *);
+
+ void setVisible(bool);
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ bool rightJustification() const;
+ bool usesBigPixmaps() const;
+ bool usesTextLabel() const;
+ bool dockWindowsMovable() const;
+ bool opaqueMoving() const;
+
+ bool eventFilter(QObject*, QEvent*);
+
+ bool getLocation(Q3DockWindow *tb, Qt::Dock &dock, int &index, bool &nl, int &extraOffset) const;
+
+ QList<Q3DockWindow *> dockWindows(Qt::Dock dock) const;
+ QList<Q3DockWindow *> dockWindows() const;
+ void lineUpDockWindows(bool keepNewLines = false);
+
+ bool isDockMenuEnabled() const;
+
+ // compatibility stuff
+ bool hasDockWindow(Q3DockWindow *dw);
+#ifndef QT_NO_TOOLBAR
+ void addToolBar(Q3DockWindow *, Qt::Dock = Qt::DockTop, bool newLine = false);
+ void addToolBar(Q3DockWindow *, const QString &label,
+ Qt::Dock = Qt::DockTop, bool newLine = false);
+ void moveToolBar(Q3DockWindow *, Qt::Dock = Qt::DockTop);
+ void moveToolBar(Q3DockWindow *, Qt::Dock, bool nl, int index, int extraOffset = -1);
+ void removeToolBar(Q3DockWindow *);
+
+ bool toolBarsMovable() const;
+ QList<Q3ToolBar *> toolBars(Qt::Dock dock) const;
+ void lineUpToolBars(bool keepNewLines = false);
+#endif
+
+ virtual Q3DockArea *dockingArea(const QPoint &p);
+ Q3DockArea *leftDock() const;
+ Q3DockArea *rightDock() const;
+ Q3DockArea *topDock() const;
+ Q3DockArea *bottomDock() const;
+
+ virtual bool isCustomizable() const;
+
+ bool appropriate(Q3DockWindow *dw) const;
+
+ enum DockWindows { OnlyToolBars, NoToolBars, AllDockWindows };
+ virtual Q3PopupMenu *createDockWindowMenu(DockWindows dockWindows = AllDockWindows) const;
+
+public Q_SLOTS:
+ virtual void setRightJustification(bool);
+ virtual void setUsesBigPixmaps(bool);
+ virtual void setUsesTextLabel(bool);
+ virtual void setDockWindowsMovable(bool);
+ virtual void setOpaqueMoving(bool);
+ virtual void setDockMenuEnabled(bool);
+ virtual void whatsThis();
+ virtual void setAppropriate(Q3DockWindow *dw, bool a);
+ virtual void customize();
+
+ // compatibility stuff
+ void setToolBarsMovable(bool);
+
+Q_SIGNALS:
+ void pixmapSizeChanged(bool);
+ void usesTextLabelChanged(bool);
+ void dockWindowPositionChanged(Q3DockWindow *);
+
+#ifndef QT_NO_TOOLBAR
+ // compatibility stuff
+ void toolBarPositionChanged(Q3ToolBar *);
+#endif
+
+protected Q_SLOTS:
+ virtual void setUpLayout();
+ virtual bool showDockMenu(const QPoint &globalPos);
+ void menuAboutToShow();
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void childEvent(QChildEvent *);
+ bool event(QEvent *);
+
+private Q_SLOTS:
+ void slotPlaceChanged();
+ void doLineUp() { lineUpDockWindows(true); }
+
+private:
+ Q_DISABLE_COPY(Q3MainWindow)
+
+ void triggerLayout(bool deleteLayout = true);
+ bool dockMainWindow(QObject *dock) const;
+
+#ifndef QT_NO_MENUBAR
+ virtual void setMenuBar(QMenuBar *);
+#endif
+ virtual void setStatusBar(QStatusBar *);
+#if 0
+ virtual void setToolTipGroup(QToolTipGroup *);
+#endif
+
+ friend class Q3DockWindow;
+ friend class QMenuBarPrivate;
+ friend class QHideDock;
+ friend class Q3ToolBar;
+ friend class Q3MainWindowLayout;
+};
+
+#ifndef QT_NO_TOOLBAR
+inline void Q3MainWindow::addToolBar(Q3DockWindow *w, Qt::ToolBarDock dock, bool newLine)
+{
+ addDockWindow(w, dock, newLine);
+}
+
+inline void Q3MainWindow::addToolBar(Q3DockWindow *w, const QString &label,
+ Qt::ToolBarDock dock, bool newLine)
+{
+ addDockWindow(w, label, dock, newLine);
+}
+
+inline void Q3MainWindow::moveToolBar(Q3DockWindow *w, Qt::ToolBarDock dock)
+{
+ moveDockWindow(w, dock);
+}
+
+inline void Q3MainWindow::moveToolBar(Q3DockWindow *w, Qt::ToolBarDock dock, bool nl, int index, int extraOffset)
+{
+ moveDockWindow(w, dock, nl, index, extraOffset);
+}
+
+inline void Q3MainWindow::removeToolBar(Q3DockWindow *w)
+{
+ removeDockWindow(w);
+}
+
+inline bool Q3MainWindow::toolBarsMovable() const
+{
+ return dockWindowsMovable();
+}
+
+inline void Q3MainWindow::lineUpToolBars(bool keepNewLines)
+{
+ lineUpDockWindows(keepNewLines);
+}
+
+inline void Q3MainWindow::setToolBarsMovable(bool b)
+{
+ setDockWindowsMovable(b);
+}
+#endif
+
+#ifndef QT_NO_TEXTSTREAM
+Q_COMPAT_EXPORT QTextStream &operator<<(QTextStream &, const Q3MainWindow &);
+Q_COMPAT_EXPORT QTextStream &operator>>(QTextStream &, Q3MainWindow &);
+#endif
+
+#endif // QT_NO_MAINWINDOW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3MAINWINDOW_H
diff --git a/src/qt3support/widgets/q3mainwindow_p.h b/src/qt3support/widgets/q3mainwindow_p.h
new file mode 100644
index 0000000..d17a1ba
--- /dev/null
+++ b/src/qt3support/widgets/q3mainwindow_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3MAINWINDOW_P_H
+#define Q3MAINWINDOW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3MainWindowLayout;
+
+class Q3MainWindowPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(Q3MainWindow)
+public:
+ Q3MainWindowPrivate()
+ : mb(0), sb(0), ttg(0), mc(0), tll(0), mwl(0), ubp(false), utl(false),
+ justify(false), movable(true), opaque(false), dockMenu(true)
+ {
+ docks.insert(Qt::DockTop, true);
+ docks.insert(Qt::DockBottom, true);
+ docks.insert(Qt::DockLeft, true);
+ docks.insert(Qt::DockRight, true);
+ docks.insert(Qt::DockMinimized, false);
+ docks.insert(Qt::DockTornOff, true);
+ }
+
+ ~Q3MainWindowPrivate()
+ {
+ }
+
+#ifndef QT_NO_MENUBAR
+ mutable QMenuBar * mb;
+#else
+ mutable QWidget * mb;
+#endif
+ QStatusBar * sb;
+ QToolTipGroup * ttg;
+
+ QWidget * mc;
+
+ QBoxLayout * tll;
+ Q3MainWindowLayout * mwl;
+
+ uint ubp :1;
+ uint utl :1;
+ uint justify :1;
+ uint movable :1;
+ uint opaque :1;
+ uint dockMenu :1;
+
+ Q3DockArea *topDock, *bottomDock, *leftDock, *rightDock;
+
+ QList<Q3DockWindow *> dockWindows;
+ QMap<Qt::Dock, bool> docks;
+ QStringList disabledDocks;
+ QHideDock *hideDock;
+
+ QPointer<Q3PopupMenu> rmbMenu, tbMenu, dwMenu;
+ QMap<Q3DockWindow*, bool> appropriate;
+ mutable QMap<Q3PopupMenu*, Q3MainWindow::DockWindows> dockWindowModes;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3MAINWINDOW_P_H
diff --git a/src/qt3support/widgets/q3popupmenu.cpp b/src/qt3support/widgets/q3popupmenu.cpp
new file mode 100644
index 0000000..78b400a
--- /dev/null
+++ b/src/qt3support/widgets/q3popupmenu.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "q3popupmenu.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3PopupMenu
+ \brief The Q3PopupMenu class is a thin compatibility wrapper around QMenu.
+ \compat
+
+ Use QMenu in new applications. Note that the menu's actions must
+ be \l {Q3Action}s.
+*/
+
+/*!
+ \fn Q3PopupMenu::Q3PopupMenu(QWidget *parent, const char *name)
+
+ Constructs a menu with the given \a parent and \a name.
+*/
+
+/*!
+ \fn int Q3PopupMenu::exec()
+
+ Pops up the menu and returns the ID of the action that was
+ selected.
+
+ \sa QMenu::exec()
+*/
+
+/*!
+ \fn int Q3PopupMenu::exec(const QPoint & pos, int indexAtPoint)
+
+ Pops up the menu at coordinate \a pos and returns the ID of the
+ action that was selected.
+
+ If \a indexAtPoint is specified, the menu will pop up with the
+ item at index \a indexAtPoint under the mouse cursor.
+
+ \sa QMenu::exec()
+*/
+
+
+/*!
+ \fn void Q3PopupMenu::setFrameRect(QRect)
+ \internal
+*/
+
+/*!
+ \fn QRect Q3PopupMenu::frameRect() const
+ \internal
+*/
+/*!
+ \enum Q3PopupMenu::DummyFrame
+ \internal
+
+ \value Box
+ \value Sunken
+ \value Plain
+ \value Raised
+ \value MShadow
+ \value NoFrame
+ \value Panel
+ \value StyledPanel
+ \value HLine
+ \value VLine
+ \value GroupBoxPanel
+ \value WinPanel
+ \value ToolBarPanel
+ \value MenuBarPanel
+ \value PopupPanel
+ \value LineEditPanel
+ \value TabWidgetPanel
+ \value MShape
+*/
+
+/*!
+ \fn void Q3PopupMenu::setFrameShadow(DummyFrame)
+ \internal
+*/
+
+/*!
+ \fn DummyFrame Q3PopupMenu::frameShadow() const
+ \internal
+*/
+
+/*!
+ \fn void Q3PopupMenu::setFrameShape(DummyFrame)
+ \internal
+*/
+
+/*!
+ \fn DummyFrame Q3PopupMenu::frameShape() const
+ \internal
+*/
+
+/*!
+ \fn void Q3PopupMenu::setFrameStyle(int)
+ \internal
+*/
+
+/*!
+ \fn int Q3PopupMenu::frameStyle() const
+ \internal
+*/
+
+/*!
+ \fn int Q3PopupMenu::frameWidth() const
+ \internal
+*/
+
+/*!
+ \fn void Q3PopupMenu::setLineWidth(int)
+ \internal
+*/
+
+/*!
+ \fn int Q3PopupMenu::lineWidth() const
+ \internal
+*/
+
+/*!
+ \fn void Q3PopupMenu::setMargin(int margin)
+ \since 4.2
+
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ This function uses QWidget::setContentsMargins() to set the margin.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int Q3PopupMenu::margin() const
+ \since 4.2
+
+ Returns the width of the margin around the contents of the widget.
+
+ This function uses QWidget::getContentsMargins() to get the margin.
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+/*!
+ \fn void Q3PopupMenu::setMidLineWidth(int)
+ \internal
+*/
+
+/*!
+ \fn int Q3PopupMenu::midLineWidth() const
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3popupmenu.h b/src/qt3support/widgets/q3popupmenu.h
new file mode 100644
index 0000000..4238692
--- /dev/null
+++ b/src/qt3support/widgets/q3popupmenu.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3POPUPMENU_H
+#define Q3POPUPMENU_H
+
+#include <QtGui/qmenu.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3PopupMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ inline Q3PopupMenu(QWidget *parent = 0, const char *name = 0) : QMenu(parent)
+ { setObjectName(QLatin1String(name)); }
+
+ inline int exec() { return findIdForAction(QMenu::exec()); }
+ inline int exec(const QPoint & pos, int indexAtPoint = 0) {
+ return findIdForAction(QMenu::exec(pos, actions().value(indexAtPoint)));
+ }
+
+ void setFrameRect(QRect) {}
+ QRect frameRect() const { return QRect(); }
+ enum DummyFrame { Box, Sunken, Plain, Raised, MShadow, NoFrame, Panel, StyledPanel,
+ HLine, VLine, GroupBoxPanel, WinPanel, ToolBarPanel, MenuBarPanel,
+ PopupPanel, LineEditPanel, TabWidgetPanel, MShape };
+ void setFrameShadow(DummyFrame) {}
+ DummyFrame frameShadow() const { return Plain; }
+ void setFrameShape(DummyFrame) {}
+ DummyFrame frameShape() const { return NoFrame; }
+ void setFrameStyle(int) {}
+ int frameStyle() const { return 0; }
+ int frameWidth() const { return 0; }
+ void setLineWidth(int) {}
+ int lineWidth() const { return 0; }
+ void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+ void setMidLineWidth(int) {}
+ int midLineWidth() const { return 0; }
+
+private:
+ Q_DISABLE_COPY(Q3PopupMenu)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPOPUPMENU_H
diff --git a/src/qt3support/widgets/q3progressbar.cpp b/src/qt3support/widgets/q3progressbar.cpp
new file mode 100644
index 0000000..dc3ccb9
--- /dev/null
+++ b/src/qt3support/widgets/q3progressbar.cpp
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3progressbar.h"
+#ifndef QT_NO_PROGRESSBAR
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qpixmap.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#include "qevent.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3ProgressBar
+ \brief The Q3ProgressBar widget provides a horizontal progress bar.
+
+ \compat
+
+ A progress bar is used to give the user an indication of the
+ progress of an operation and to reassure them that the application
+ is still running.
+
+ The progress bar uses the concept of \e steps; you give it the
+ total number of steps and the number of steps completed so far and
+ it will display the percentage of steps that have been completed.
+ You can specify the total number of steps in the constructor or
+ later with setTotalSteps(). The current number of steps is set
+ with setProgress(). The progress bar can be rewound to the
+ beginning with reset().
+
+ If the total is given as 0 the progress bar shows a busy indicator
+ instead of a percentage of steps. This is useful, for example,
+ when using QFtp or QHttp to download items when they are unable to
+ determine the size of the item being downloaded.
+
+ \sa QProgressDialog
+
+ \inlineimage qprogbar-m.png Screenshot in Motif style
+ \inlineimage qprogbar-w.png Screenshot in Windows style
+
+ \sa QProgressDialog
+*/
+
+
+/*! \obsolete
+ Constructs a progress bar.
+
+ The total number of steps is set to 100 by default.
+
+ The \a parent, \a name and widget flags, \a f, are passed on to
+ the QFrame::QFrame() constructor.
+
+ \sa setTotalSteps()
+*/
+
+Q3ProgressBar::Q3ProgressBar(QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QFrame(parent, f),
+ total_steps(100),
+ progress_val(-1),
+ percentage(-1),
+ center_indicator(true),
+ percentage_visible(true),
+ d(0)
+{
+ setObjectName(QLatin1String(name));
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ initFrame();
+}
+
+
+/*! \obsolete
+ Constructs a progress bar.
+
+ The \a totalSteps is the total number of steps that need to be
+ completed for the operation which this progress bar represents.
+ For example, if the operation is to examine 50 files, this value
+ would be 50. Before examining the first file, call setProgress(0);
+ call setProgress(50) after examining the last file.
+
+ The \a parent, \a name and widget flags, \a f, are passed to the
+ QFrame::QFrame() constructor.
+
+ \sa setTotalSteps(), setProgress()
+*/
+
+Q3ProgressBar::Q3ProgressBar(int totalSteps, QWidget *parent, const char *name, Qt::WindowFlags f)
+ : QFrame(parent, f),
+ total_steps(totalSteps),
+ progress_val(-1),
+ percentage(-1),
+ center_indicator(true),
+ percentage_visible(true),
+ d(0)
+{
+ setObjectName(QLatin1String(name));
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ initFrame();
+}
+#endif
+
+/*!
+ Constructs a progress bar.
+
+ The total number of steps is set to 100 by default.
+
+ The \a parent, and widget flags, \a f, are passed on to
+ the QFrame::QFrame() constructor.
+
+ \sa setTotalSteps()
+*/
+
+Q3ProgressBar::Q3ProgressBar(QWidget *parent, Qt::WindowFlags f)
+ : QFrame(parent, f),
+ total_steps(100),
+ progress_val(-1),
+ percentage(-1),
+ center_indicator(true),
+ percentage_visible(true),
+ d(0)
+{
+ setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+ initFrame();
+}
+
+
+/*!
+ Constructs a progress bar.
+
+ The \a totalSteps is the total number of steps that need to be
+ completed for the operation which this progress bar represents.
+ For example, if the operation is to examine 50 files, this value
+ would be 50. Before examining the first file, call setProgress(0);
+ call setProgress(50) after examining the last file.
+
+ The \a parent, and widget flags, \a f, are passed to the
+ QFrame::QFrame() constructor.
+
+ \sa setTotalSteps(), setProgress()
+*/
+
+Q3ProgressBar::Q3ProgressBar(int totalSteps, QWidget *parent, Qt::WindowFlags f)
+ : QFrame(parent, f),
+ total_steps(totalSteps),
+ progress_val(-1),
+ percentage(-1),
+ center_indicator(true),
+ percentage_visible(true),
+ d(0)
+{
+ setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+ initFrame();
+}
+
+
+/*!
+ Reset the progress bar. The progress bar "rewinds" and shows no
+ progress.
+*/
+
+void Q3ProgressBar::reset()
+{
+ progress_val = -1;
+ percentage = -1;
+ setIndicator(progress_str, progress_val, total_steps);
+ repaint();
+}
+
+
+/*!
+ \property Q3ProgressBar::totalSteps
+ \brief The total number of steps.
+
+ If totalSteps is 0, the progress bar will display a busy
+ indicator.
+*/
+
+void Q3ProgressBar::setTotalSteps(int totalSteps)
+{
+ total_steps = totalSteps;
+
+ // Current progress is invalid if larger than total
+ if (total_steps < progress_val)
+ progress_val = -1;
+
+ if (isVisible() &&
+ (setIndicator(progress_str, progress_val, total_steps) || !total_steps))
+ repaint();
+}
+
+
+/*!
+ \property Q3ProgressBar::progress
+ \brief The current amount of progress
+
+ This property is -1 if progress counting has not started.
+*/
+
+void Q3ProgressBar::setProgress(int progress)
+{
+ if (progress == progress_val ||
+ progress < 0 || ((progress > total_steps) && total_steps))
+ return;
+
+ int old_progress_val = progress_val;
+ progress_val = progress;
+
+ if (setIndicator(progress_str, progress_val, total_steps)
+ || ( total_steps == 0 || (width() * progress_val / total_steps != width() * old_progress_val / total_steps )))
+ repaint();
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::ValueChanged);
+#endif
+}
+
+/*!
+ \overload
+
+ Sets the amount of progress to \a progress and the total number of
+ steps to \a totalSteps.
+
+ \sa setTotalSteps()
+*/
+
+void Q3ProgressBar::setProgress(int progress, int totalSteps)
+{
+ if (total_steps != totalSteps)
+ setTotalSteps(totalSteps);
+ setProgress(progress);
+}
+
+/*!
+ \property Q3ProgressBar::progressString
+ \brief the amount of progress as a string
+
+ This property is an empty string if progress counting has not started.
+*/
+
+static QStyleOptionProgressBar getStyleOption(const Q3ProgressBar *pb)
+{
+ QStyleOptionProgressBar opt;
+ opt.init(pb);
+ opt.rect = pb->contentsRect();
+ opt.minimum = 0;
+ opt.maximum = pb->totalSteps();
+ opt.progress = pb->progress();
+ if (pb->centerIndicator())
+ opt.textAlignment = Qt::AlignCenter;
+ else
+ opt.textAlignment = Qt::AlignAuto;
+ opt.textVisible = pb->percentageVisible();
+ opt.text = pb->progressString();
+ return opt;
+}
+
+/*!
+ \reimp
+*/
+QSize Q3ProgressBar::sizeHint() const
+{
+ ensurePolished();
+ QFontMetrics fm = fontMetrics();
+ QStyleOptionProgressBar opt = getStyleOption(this);
+ int cw = style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, this);
+ return style()->sizeFromContents(QStyle::CT_ProgressBar, &opt,
+ QSize(cw * 7 + fm.width(QLatin1Char('0')) * 4, fm.height() + 8), this);
+}
+
+/*!
+ \reimp
+*/
+QSize Q3ProgressBar::minimumSizeHint() const
+{
+ return sizeHint();
+}
+
+/*!
+ \property Q3ProgressBar::centerIndicator
+ \brief whether the indicator string should be centered
+*/
+
+void Q3ProgressBar::setCenterIndicator(bool on)
+{
+ if (on == center_indicator)
+ return;
+ center_indicator = on;
+ repaint();
+}
+
+/*!
+ \property Q3ProgressBar::percentageVisible
+ \brief whether the current progress value is displayed
+
+ The default is true.
+
+ \sa centerIndicator
+*/
+void Q3ProgressBar::setPercentageVisible(bool on)
+{
+ if (on == percentage_visible)
+ return;
+ percentage_visible = on;
+ repaint();
+}
+
+/*!
+ \reimp
+*/
+void Q3ProgressBar::setVisible(bool visible)
+{
+ if (visible)
+ setIndicator(progress_str, progress_val, total_steps);
+ QFrame::setVisible(visible);
+}
+
+void Q3ProgressBar::initFrame()
+{
+ setFrameStyle(QFrame::NoFrame);
+}
+
+/*!
+ \reimp
+*/
+void Q3ProgressBar::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange)
+ initFrame();
+ QFrame::changeEvent(ev);
+}
+
+
+/*!
+ This method is called to generate the text displayed in the center
+ (or in some styles, to the left) of the progress bar.
+
+ The \a progress may be negative, indicating that the progress bar
+ is in the "reset" state before any progress is set.
+
+ The default implementation is the percentage of completion or
+ blank in the reset state. The percentage is calculated based on
+ the \a progress and \a totalSteps. You can set the \a indicator
+ text if you wish.
+
+ To allow efficient repainting of the progress bar, this method
+ should return false if the string is unchanged from the last call
+ to this function.
+*/
+
+bool Q3ProgressBar::setIndicator(QString & indicator, int progress,
+ int totalSteps)
+{
+ if (!totalSteps)
+ return false;
+ if (progress < 0) {
+ indicator = QString::fromLatin1("");
+ return true;
+ } else {
+ // Get the values down to something usable.
+ if (totalSteps > INT_MAX/1000) {
+ progress /= 1000;
+ totalSteps /= 1000;
+ }
+
+ int np = progress * 100 / totalSteps;
+ if (np != percentage) {
+ percentage = np;
+ indicator.sprintf("%d%%", np);
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void Q3ProgressBar::paintEvent(QPaintEvent *)
+{
+ QPainter paint(this);
+ QPainter *p = &paint;
+ drawFrame(p);
+
+ QStyleOptionProgressBar opt = getStyleOption(this);
+ opt.rect = style()->subElementRect(QStyle::SE_ProgressBarGroove, &opt, this);
+
+ style()->drawControl(QStyle::CE_ProgressBarGroove, &opt, p, this);
+ opt.rect = contentsRect();
+ opt.rect = style()->subElementRect(QStyle::SE_ProgressBarContents, &opt, this);
+ style()->drawControl(QStyle::CE_ProgressBarContents, &opt, p, this);
+
+ if (percentageVisible()) {
+ opt.rect = contentsRect();
+ opt.rect = style()->subElementRect(QStyle::SE_ProgressBarLabel, &opt, this);
+ style()->drawControl(QStyle::CE_ProgressBarLabel, &opt, p, this);
+ }
+}
+
+/*!
+ \fn void Q3ProgressBar::setMargin(int margin)
+ \since 4.2
+
+ Sets the width of the margin around the contents of the widget to \a margin.
+
+ This function uses QWidget::setContentsMargins() to set the margin.
+ \sa margin(), QWidget::setContentsMargins()
+*/
+
+/*!
+ \fn int Q3ProgressBar::margin() const
+ \since 4.2
+
+ Returns the width of the margin around the contents of the widget.
+
+ This function uses QWidget::getContentsMargins() to get the margin.
+ \sa setMargin(), QWidget::getContentsMargins()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3progressbar.h b/src/qt3support/widgets/q3progressbar.h
new file mode 100644
index 0000000..cc1887f
--- /dev/null
+++ b/src/qt3support/widgets/q3progressbar.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3PROGRESSBAR_H
+#define Q3PROGRESSBAR_H
+
+#include <QtGui/qframe.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_PROGRESSBAR
+
+class Q3ProgressBarPrivate;
+
+class Q_COMPAT_EXPORT Q3ProgressBar : public QFrame
+{
+ Q_OBJECT
+ Q_PROPERTY(int totalSteps READ totalSteps WRITE setTotalSteps)
+ Q_PROPERTY(int progress READ progress WRITE setProgress)
+ Q_PROPERTY(QString progressString READ progressString)
+ Q_PROPERTY(bool centerIndicator READ centerIndicator WRITE setCenterIndicator)
+ Q_PROPERTY(bool percentageVisible READ percentageVisible WRITE setPercentageVisible)
+
+public:
+ Q3ProgressBar(QWidget *parent, const char *name, Qt::WindowFlags f=0);
+ Q3ProgressBar(int totalSteps, QWidget *parent, const char *name,
+ Qt::WindowFlags f=0);
+ Q3ProgressBar(QWidget *parent = 0, Qt::WindowFlags f = 0);
+ Q3ProgressBar(int totalSteps, QWidget *parent = 0, Qt::WindowFlags f=0);
+
+ int totalSteps() const;
+ int progress() const;
+ const QString &progressString() const;
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ void setCenterIndicator(bool on);
+ bool centerIndicator() const;
+
+ bool percentageVisible() const;
+ void setPercentageVisible(bool);
+
+ void setVisible(bool visible);
+
+ void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); }
+ int margin() const
+ { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; }
+
+public Q_SLOTS:
+ void reset();
+ virtual void setTotalSteps(int totalSteps);
+ virtual void setProgress(int progress);
+ void setProgress(int progress, int totalSteps);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ virtual bool setIndicator(QString &progress_str, int progress, int totalSteps);
+ void changeEvent(QEvent *);
+
+private:
+ Q_DISABLE_COPY(Q3ProgressBar)
+
+ int total_steps;
+ int progress_val;
+ int percentage;
+ QString progress_str;
+ bool center_indicator : 1;
+ bool percentage_visible : 1;
+ Q3ProgressBarPrivate *d;
+ void initFrame();
+};
+
+
+inline int Q3ProgressBar::totalSteps() const
+{
+ return total_steps;
+}
+
+inline int Q3ProgressBar::progress() const
+{
+ return progress_val;
+}
+
+inline const QString &Q3ProgressBar::progressString() const
+{
+ return progress_str;
+}
+
+inline bool Q3ProgressBar::centerIndicator() const
+{
+ return center_indicator;
+}
+
+inline bool Q3ProgressBar::percentageVisible() const
+{
+ return percentage_visible;
+}
+
+#endif // QT_NO_PROGRESSBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3PROGRESSBAR_H
diff --git a/src/qt3support/widgets/q3rangecontrol.cpp b/src/qt3support/widgets/q3rangecontrol.cpp
new file mode 100644
index 0000000..39f05c1
--- /dev/null
+++ b/src/qt3support/widgets/q3rangecontrol.cpp
@@ -0,0 +1,550 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3rangecontrol.h"
+#ifndef QT_NO_RANGECONTROL
+#include "qglobal.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3RangeControl
+ \brief The Q3RangeControl class provides an integer value within a range.
+
+ \compat
+
+ Although originally designed for the QScrollBar widget, the
+ Q3RangeControl can also be used in conjunction with other widgets
+ such as QSlider and QSpinBox. Here are the five main concepts in
+ the class:
+
+ \list 1
+
+ \i \e{Current value} The bounded integer that
+ Q3RangeControl maintains. value() returns it, and several
+ functions, including setValue(), set it.
+
+ \i \e{Minimum} The lowest value that value() can ever
+ return. Returned by minValue() and set by setRange() or one of the
+ constructors.
+
+ \i \e{Maximum} The highest value that value() can ever
+ return. Returned by maxValue() and set by setRange() or one of the
+ constructors.
+
+ \i \e{Line step} The smaller of two natural steps that
+ Q3RangeControl provides and typically corresponds to the user
+ pressing an arrow key. The line step is returned by lineStep()
+ and set using setSteps(). The functions addLine() and
+ subtractLine() respectively increment and decrement the current
+ value by lineStep().
+
+ \i \e{Page step} The larger of two natural steps that
+ Q3RangeControl provides and typically corresponds to the user
+ pressing PageUp or PageDown. The page step is returned by
+ pageStep() and set using setSteps(). The functions addPage() and
+ substractPage() respectively increment and decrement the current
+ value by pageStep().
+
+ \endlist
+
+ Unity (1) may be viewed as a third step size. setValue() lets you
+ set the current value to any integer in the allowed range, not
+ just minValue() + \e n * lineStep() for integer values of \e n.
+ Some widgets may allow the user to set any value at all; others
+ may just provide multiples of lineStep() or pageStep().
+
+ Q3RangeControl provides three virtual functions that are well
+ suited for updating the on-screen representation of range controls
+ and emitting signals: valueChange(), rangeChange() and
+ stepChange().
+
+ Q3RangeControl also provides a function called bound() which lets
+ you force arbitrary integers to be within the allowed range of the
+ range control.
+
+ We recommend that all widgets that inherit Q3RangeControl provide
+ at least a signal called valueChanged(); many widgets will want to
+ provide addStep(), addPage(), substractStep() and substractPage()
+ as slots.
+
+ Note that you must use multiple inheritance if you plan to
+ implement a widget using Q3RangeControl because Q3RangeControl is
+ not derived from QWidget.
+*/
+
+
+/*!
+ Constructs a range control with a minimum value of 0, maximum
+ value of 99, line step of 1, page step of 10 and initial value 0.
+*/
+
+Q3RangeControl::Q3RangeControl()
+{
+ minVal = 0;
+ maxVal = 99;
+ line = 1;
+ page = 10;
+ val = 0;
+ prevVal = -1;
+ d = 0;
+}
+
+/*!
+ Constructs a range control whose value can never be smaller than
+ \a minValue or greater than \a maxValue, whose line step size is
+ \a lineStep and page step size is \a pageStep and whose value is
+ initially \a value (which is guaranteed to be in range using
+ bound()).
+*/
+
+Q3RangeControl::Q3RangeControl(int minValue, int maxValue,
+ int lineStep, int pageStep,
+ int value)
+{
+ minVal = minValue;
+ maxVal = maxValue;
+ line = QABS(lineStep);
+ page = QABS(pageStep);
+ prevVal = minVal - 1;
+ val = bound(value);
+ d = 0;
+}
+
+/*!
+ Destroys the range control
+*/
+
+Q3RangeControl::~Q3RangeControl()
+{
+}
+
+
+/*!
+ \fn int Q3RangeControl::value() const
+
+ Returns the current range control value. This is guaranteed to be
+ within the range [minValue(), maxValue()].
+
+ \sa setValue() prevValue()
+*/
+
+/*!
+ \fn int Q3RangeControl::prevValue() const
+
+ Returns the previous value of the range control. "Previous value"
+ means the value before the last change occurred. Setting a new
+ range may affect the value, too, because the value is forced to be
+ inside the specified range. When the range control is initially
+ created, this is the same as value().
+
+ prevValue() can be outside the current legal range if a call to
+ setRange() causes the current value to change. For example, if the
+ range was [0, 1000] and the current value is 500, setRange(0, 400)
+ makes value() return 400 and prevValue() return 500.
+
+ \sa value() setRange()
+*/
+
+/*!
+ Sets the range control's value to \a value and forces it to be
+ within the legal range.
+
+ Calls the virtual valueChange() function if the new value is
+ different from the previous value. The old value can still be
+ retrieved using prevValue().
+
+ \sa value()
+*/
+
+void Q3RangeControl::setValue(int value)
+{
+ directSetValue(value);
+ if (prevVal != val)
+ valueChange();
+}
+
+/*!
+ Sets the range control \a value directly without calling
+ valueChange().
+
+ Forces the new \a value to be within the legal range.
+
+ You will rarely have to call this function. However, if you want
+ to change the range control's value inside the overloaded method
+ valueChange(), setValue() would call the function valueChange()
+ again. To avoid this recursion you must use directSetValue()
+ instead.
+
+ \sa setValue()
+*/
+
+void Q3RangeControl::directSetValue(int value)
+{
+ prevVal = val;
+ val = bound(value);
+}
+
+/*!
+ Equivalent to \c{setValue(value() + pageStep())}.
+
+ If the value is changed, then valueChange() is called.
+
+ \sa subtractPage() addLine() setValue()
+*/
+
+void Q3RangeControl::addPage()
+{
+ setValue(value() + pageStep());
+}
+
+/*!
+ Equivalent to \c{setValue(value() - pageStep())}.
+
+ If the value is changed, then valueChange() is called.
+
+ \sa addPage() subtractLine() setValue()
+*/
+
+void Q3RangeControl::subtractPage()
+{
+ setValue(value() - pageStep());
+}
+
+/*!
+ Equivalent to \c{setValue(value() + lineStep())}.
+
+ If the value is changed, then valueChange() is called.
+
+ \sa subtractLine() addPage() setValue()
+*/
+
+void Q3RangeControl::addLine()
+{
+ setValue(value() + lineStep());
+}
+
+/*!
+ Equivalent to \c{setValue(value() - lineStep())}.
+
+ If the value is changed, then valueChange() is called.
+
+ \sa addLine() subtractPage() setValue()
+*/
+
+void Q3RangeControl::subtractLine()
+{
+ setValue(value() - lineStep());
+}
+
+
+/*!
+ \fn int Q3RangeControl::minValue() const
+
+ Returns the minimum value of the range.
+
+ \sa setMinValue() setRange() maxValue()
+*/
+
+/*!
+ \fn int Q3RangeControl::maxValue() const
+
+ Returns the maximum value of the range.
+
+ \sa setMaxValue() setRange() minValue()
+*/
+
+/*!
+ Sets the minimum value of the range to \a minVal.
+
+ If necessary, the maxValue() is adjusted so that the range remains
+ valid.
+
+ \sa minValue() setMaxValue()
+*/
+void Q3RangeControl::setMinValue(int minVal)
+{
+ int maxVal = maxValue();
+ if (maxVal < minVal)
+ maxVal = minVal;
+ setRange(minVal, maxVal);
+}
+
+/*!
+ Sets the minimum value of the range to \a maxVal.
+
+ If necessary, the minValue() is adjusted so that the range remains
+ valid.
+
+ \sa maxValue() setMinValue()
+*/
+void Q3RangeControl::setMaxValue(int maxVal)
+{
+ int minVal = minValue();
+ if (minVal > maxVal)
+ minVal = maxVal;
+ setRange(minVal, maxVal);
+}
+
+/*!
+ Sets the range control's minimum value to \a minValue and its
+ maximum value to \a maxValue.
+
+ Calls the virtual rangeChange() function if one or both of the new
+ minimum and maximum values are different from the previous
+ setting. Calls the virtual valueChange() function if the current
+ value is adjusted because it was outside the new range.
+
+ If \a maxValue is smaller than \a minValue, \a minValue becomes
+ the only legal value.
+
+ \sa minValue() maxValue()
+*/
+
+void Q3RangeControl::setRange(int minValue, int maxValue)
+{
+ if (minValue > maxValue) {
+ qWarning("Q3RangeControl::setRange: minValue %d > maxValue %d",
+ minValue, maxValue);
+ maxValue = minValue;
+ }
+ if (minValue == minVal && maxValue == maxVal)
+ return;
+ minVal = minValue;
+ maxVal = maxValue;
+ int tmp = bound(val);
+ rangeChange();
+ if (tmp != val) {
+ prevVal = val;
+ val = tmp;
+ valueChange();
+ }
+}
+
+
+/*!
+ \fn int Q3RangeControl::lineStep() const
+
+ Returns the line step.
+
+ \sa setSteps() pageStep()
+*/
+
+/*!
+ \fn int Q3RangeControl::pageStep() const
+
+ Returns the page step.
+
+ \sa setSteps() lineStep()
+*/
+
+/*!
+ Sets the range's line step to \a lineStep and page step to \a
+ pageStep.
+
+ Calls the virtual stepChange() function if the new line step
+ or page step are different from the previous settings.
+
+ \sa lineStep() pageStep() setRange()
+*/
+
+void Q3RangeControl::setSteps(int lineStep, int pageStep)
+{
+ if (lineStep != line || pageStep != page) {
+ line = QABS(lineStep);
+ page = QABS(pageStep);
+ stepChange();
+ }
+}
+
+
+/*!
+ This virtual function is called whenever the range control value
+ changes. You can reimplement it if you want to be notified when
+ the value changes. The default implementation does nothing.
+
+ Note that this method is called after the value has changed. The
+ previous value can be retrieved using prevValue().
+
+ \sa setValue(), addPage(), subtractPage(), addLine(),
+ subtractLine() rangeChange(), stepChange()
+*/
+
+void Q3RangeControl::valueChange()
+{
+}
+
+
+/*!
+ This virtual function is called whenever the range control's range
+ changes. You can reimplement it if you want to be notified when
+ the range changes. The default implementation does nothing.
+
+ Note that this method is called after the range has changed.
+
+ \sa setRange(), valueChange(), stepChange()
+*/
+
+void Q3RangeControl::rangeChange()
+{
+}
+
+
+/*!
+ This virtual function is called whenever the range control's
+ line or page step settings change. You can reimplement it if you
+ want to be notified when the step changes. The default
+ implementation does nothing.
+
+ Note that this method is called after a step setting has changed.
+
+ \sa setSteps(), rangeChange(), valueChange()
+*/
+
+void Q3RangeControl::stepChange()
+{
+}
+
+
+/*!
+ Forces the value \a v to be within the range from minValue() to
+ maxValue() inclusive, and returns the result.
+
+ This function is provided so that you can easily force other
+ numbers than value() into the allowed range. You do not need to
+ call it in order to use Q3RangeControl itself.
+
+ \sa setValue() value() minValue() maxValue()
+*/
+
+int Q3RangeControl::bound(int v) const
+{
+ if (v < minVal)
+ return minVal;
+ if (v > maxVal)
+ return maxVal;
+ return v;
+}
+
+
+/*!
+ Converts \a logical_val to a pixel position. minValue() maps to 0,
+ maxValue() maps to \a span and other values are distributed evenly
+ in-between.
+
+ This function can handle the entire integer range without
+ overflow, providing \a span is \<= 4096.
+
+ Calling this method is useful when actually drawing a range
+ control such as a QScrollBar on-screen.
+
+ \sa valueFromPosition()
+*/
+
+int Q3RangeControl::positionFromValue(int logical_val, int span) const
+{
+ if (span <= 0 || logical_val < minValue() || maxValue() <= minValue())
+ return 0;
+ if (logical_val > maxValue())
+ return span;
+
+ uint range = maxValue() - minValue();
+ uint p = logical_val - minValue();
+
+ if (range > (uint)INT_MAX/4096) {
+ const int scale = 4096*2;
+ return ((p/scale) * span) / (range/scale);
+ // ### the above line is probably not 100% correct
+ // ### but fixing it isn't worth the extreme pain...
+ } else if (range > (uint)span) {
+ return (2*p*span + range) / (2*range);
+ } else {
+ uint div = span / range;
+ uint mod = span % range;
+ return p*div + (2*p*mod + range) / (2*range);
+ }
+ //equiv. to (p*span)/range + 0.5
+ // no overflow because of this implicit assumption:
+ // span <= 4096
+}
+
+
+/*!
+ Converts the pixel position \a pos to a value. 0 maps to
+ minValue(), \a span maps to maxValue() and other values are
+ distributed evenly in-between.
+
+ This function can handle the entire integer range without
+ overflow.
+
+ Calling this method is useful if you actually implemented a range
+ control widget such as QScrollBar and want to handle mouse press
+ events. This function then maps screen coordinates to the logical
+ values.
+
+ \sa positionFromValue()
+*/
+
+int Q3RangeControl::valueFromPosition(int pos, int span) const
+{
+ if (span <= 0 || pos <= 0)
+ return minValue();
+ if (pos >= span)
+ return maxValue();
+
+ uint range = maxValue() - minValue();
+
+ if ((uint)span > range)
+ return minValue() + (2*pos*range + span) / (2*span);
+ else {
+ uint div = range / span;
+ uint mod = range % span;
+ return minValue() + pos*div + (2*pos*mod + span) / (2*span);
+ }
+ // equiv. to minValue() + (pos*range)/span + 0.5
+ // no overflow because of this implicit assumption:
+ // pos <= span < sqrt(INT_MAX+0.0625)+0.25 ~ sqrt(INT_MAX)
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RANGECONTROL
diff --git a/src/qt3support/widgets/q3rangecontrol.h b/src/qt3support/widgets/q3rangecontrol.h
new file mode 100644
index 0000000..412dc9e
--- /dev/null
+++ b/src/qt3support/widgets/q3rangecontrol.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3RANGECONTROL_H
+#define Q3RANGECONTROL_H
+
+#include <QtCore/qglobal.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_RANGECONTROL
+
+class Q3RangeControlPrivate;
+
+class Q_COMPAT_EXPORT Q3RangeControl
+{
+public:
+ Q3RangeControl();
+ Q3RangeControl(int minValue, int maxValue,
+ int lineStep, int pageStep, int value);
+ virtual ~Q3RangeControl();
+
+ int value() const;
+ void setValue(int);
+ void addPage();
+ void subtractPage();
+ void addLine();
+ void subtractLine();
+
+ int minValue() const;
+ int maxValue() const;
+ void setRange(int minValue, int maxValue);
+ void setMinValue(int minVal);
+ void setMaxValue(int minVal);
+
+ int lineStep() const;
+ int pageStep() const;
+ void setSteps(int line, int page);
+
+ int bound(int) const;
+
+protected:
+ int positionFromValue(int val, int space) const;
+ int valueFromPosition(int pos, int space) const;
+ void directSetValue(int val);
+ int prevValue() const;
+
+ virtual void valueChange();
+ virtual void rangeChange();
+ virtual void stepChange();
+
+private:
+ int minVal, maxVal;
+ int line, page;
+ int val, prevVal;
+
+ Q3RangeControlPrivate * d;
+
+private:
+ Q_DISABLE_COPY(Q3RangeControl)
+};
+
+
+inline int Q3RangeControl::value() const
+{ return val; }
+
+inline int Q3RangeControl::prevValue() const
+{ return prevVal; }
+
+inline int Q3RangeControl::minValue() const
+{ return minVal; }
+
+inline int Q3RangeControl::maxValue() const
+{ return maxVal; }
+
+inline int Q3RangeControl::lineStep() const
+{ return line; }
+
+inline int Q3RangeControl::pageStep() const
+{ return page; }
+
+
+#endif // QT_NO_RANGECONTROL
+
+#ifndef QT_NO_SPINWIDGET
+
+class Q3SpinWidgetPrivate;
+class Q_COMPAT_EXPORT Q3SpinWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ Q3SpinWidget(QWidget* parent=0, const char* name=0);
+ ~Q3SpinWidget();
+
+ void setEditWidget(QWidget * widget);
+ QWidget * editWidget();
+
+ QRect upRect() const;
+ QRect downRect() const;
+
+ void setUpEnabled(bool on);
+ void setDownEnabled(bool on);
+
+ bool isUpEnabled() const;
+ bool isDownEnabled() const;
+
+ enum ButtonSymbols { UpDownArrows, PlusMinus };
+ virtual void setButtonSymbols(ButtonSymbols bs);
+ ButtonSymbols buttonSymbols() const;
+
+ void arrange();
+
+Q_SIGNALS:
+ void stepUpPressed();
+ void stepDownPressed();
+
+public Q_SLOTS:
+ void stepUp();
+ void stepDown();
+
+protected:
+ void mousePressEvent(QMouseEvent *e);
+ void resizeEvent(QResizeEvent* ev);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+#ifndef QT_NO_WHEELEVENT
+ void wheelEvent(QWheelEvent *);
+#endif
+ void changeEvent(QEvent *);
+ void paintEvent(QPaintEvent *);
+
+private Q_SLOTS:
+ void timerDone();
+ void timerDoneEx();
+
+private:
+ Q3SpinWidgetPrivate * d;
+
+ void updateDisplay();
+
+private:
+ Q_DISABLE_COPY(Q3SpinWidget)
+};
+
+#endif // QT_NO_RANGECONTROL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3RANGECONTROL_H
diff --git a/src/qt3support/widgets/q3scrollview.cpp b/src/qt3support/widgets/q3scrollview.cpp
new file mode 100644
index 0000000..ea45d6f
--- /dev/null
+++ b/src/qt3support/widgets/q3scrollview.cpp
@@ -0,0 +1,2807 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidget.h"
+#ifndef QT_NO_SCROLLVIEW
+#include "qscrollbar.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qcursor.h"
+#include "q3scrollview.h"
+#include "q3ptrdict.h"
+#include "qapplication.h"
+#include "qtimer.h"
+#include "qstyle.h"
+#include "q3ptrlist.h"
+#include "qevent.h"
+#include "q3listview.h"
+#ifdef Q_WS_MAC
+# include "private/qt_mac_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+static const int coord_limit = 4000;
+static const int autoscroll_margin = 16;
+static const int initialScrollTime = 30;
+static const int initialScrollAccel = 5;
+
+struct QSVChildRec {
+ QSVChildRec(QWidget* c, int xx, int yy) :
+ child(c),
+ x(xx), y(yy)
+ {
+ }
+
+ void hideOrShow(Q3ScrollView* sv, QWidget* clipped_viewport);
+ void moveTo(Q3ScrollView* sv, int xx, int yy, QWidget* clipped_viewport)
+ {
+ if (x != xx || y != yy) {
+ x = xx;
+ y = yy;
+ hideOrShow(sv,clipped_viewport);
+ }
+ }
+ QWidget* child;
+ int x, y;
+};
+
+void QSVChildRec::hideOrShow(Q3ScrollView* sv, QWidget* clipped_viewport)
+{
+ if (clipped_viewport) {
+ if (x+child->width() < sv->contentsX()+clipped_viewport->x()
+ || x > sv->contentsX()+clipped_viewport->width()
+ || y+child->height() < sv->contentsY()+clipped_viewport->y()
+ || y > sv->contentsY()+clipped_viewport->height()) {
+ child->move(clipped_viewport->width(),
+ clipped_viewport->height());
+ } else {
+ child->move(x-sv->contentsX()-clipped_viewport->x(),
+ y-sv->contentsY()-clipped_viewport->y());
+ }
+ } else {
+ child->move(x-sv->contentsX(), y-sv->contentsY());
+ }
+}
+
+class QAbstractScrollAreaWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QAbstractScrollAreaWidget(Q3ScrollView* parent=0, const char* name=0, Qt::WindowFlags f = 0)
+ : QWidget(parent, name, f)
+ {
+ setAutoFillBackground(true);
+ }
+};
+
+class QClipperWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ QClipperWidget(QWidget * parent=0, const char * name=0, Qt::WindowFlags f=0)
+ : QWidget (parent,name,f) {}
+};
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "q3scrollview.moc"
+QT_END_INCLUDE_NAMESPACE
+
+class Q3ScrollViewData {
+public:
+ Q3ScrollViewData(Q3ScrollView* parent, int vpwflags) :
+ hbar(new QScrollBar(Qt::Horizontal, parent, "qt_hbar")),
+ vbar(new QScrollBar(Qt::Vertical, parent, "qt_vbar")),
+ viewport(new QAbstractScrollAreaWidget(parent, "qt_viewport", QFlag(vpwflags))),
+ clipped_viewport(0),
+ flags(vpwflags),
+ vx(0), vy(0), vwidth(1), vheight(1),
+#ifndef QT_NO_DRAGANDDROP
+ autoscroll_timer(parent, "scrollview autoscroll timer"),
+ drag_autoscroll(true),
+#endif
+ scrollbar_timer(parent, "scrollview scrollbar timer"),
+ inresize(false), use_cached_size_hint(true)
+ {
+ l_marg = r_marg = t_marg = b_marg = 0;
+ viewport->polish();
+ vMode = Q3ScrollView::Auto;
+ hMode = Q3ScrollView::Auto;
+ corner = 0;
+ vbar->setSteps(20, 1/*set later*/);
+ hbar->setSteps(20, 1/*set later*/);
+ policy = Q3ScrollView::Default;
+ signal_choke = false;
+ static_bg = false;
+ fake_scroll = false;
+ hbarPressed = false;
+ vbarPressed = false;
+ hbar->setLayoutDirection(Qt::LeftToRight);
+ }
+ ~Q3ScrollViewData();
+
+ QSVChildRec* rec(QWidget* w) { return childDict.find(w); }
+ QSVChildRec* ancestorRec(QWidget* w);
+ QSVChildRec* addChildRec(QWidget* w, int x, int y)
+ {
+ QSVChildRec *r = new QSVChildRec(w,x,y);
+ children.append(r);
+ childDict.insert(w, r);
+ return r;
+ }
+ void deleteChildRec(QSVChildRec* r)
+ {
+ childDict.remove(r->child);
+ children.removeRef(r);
+ delete r;
+ }
+
+ void hideOrShowAll(Q3ScrollView* sv, bool isScroll = false);
+ void moveAllBy(int dx, int dy);
+ bool anyVisibleChildren();
+ void autoMove(Q3ScrollView* sv);
+ void autoResize(Q3ScrollView* sv);
+ void autoResizeHint(Q3ScrollView* sv);
+ void viewportResized(int w, int h);
+
+ QScrollBar* hbar;
+ QScrollBar* vbar;
+ bool hbarPressed;
+ bool vbarPressed;
+ QAbstractScrollAreaWidget* viewport;
+ QClipperWidget* clipped_viewport;
+ int flags;
+ Q3PtrList<QSVChildRec> children;
+ Q3PtrDict<QSVChildRec> childDict;
+ QWidget* corner;
+ int vx, vy, vwidth, vheight; // for drawContents-style usage
+ int l_marg, r_marg, t_marg, b_marg;
+ Q3ScrollView::ResizePolicy policy;
+ Q3ScrollView::ScrollBarMode vMode;
+ Q3ScrollView::ScrollBarMode hMode;
+#ifndef QT_NO_DRAGANDDROP
+ QPoint cpDragStart;
+ QTimer autoscroll_timer;
+ int autoscroll_time;
+ int autoscroll_accel;
+ bool drag_autoscroll;
+#endif
+ QTimer scrollbar_timer;
+
+ uint static_bg : 1;
+ uint fake_scroll : 1;
+
+ // This variable allows ensureVisible to move the contents then
+ // update both the sliders. Otherwise, updating the sliders would
+ // cause two image scrolls, creating ugly flashing.
+ //
+ uint signal_choke : 1;
+
+ // This variables indicates in updateScrollBars() that we are
+ // in a resizeEvent() and thus don't want to flash scroll bars
+ uint inresize : 1;
+ uint use_cached_size_hint : 1;
+ QSize cachedSizeHint;
+
+ inline int contentsX() const { return -vx; }
+ inline int contentsY() const { return -vy; }
+ inline int contentsWidth() const { return vwidth; }
+};
+
+inline Q3ScrollViewData::~Q3ScrollViewData()
+{
+ children.setAutoDelete(true);
+}
+
+QSVChildRec* Q3ScrollViewData::ancestorRec(QWidget* w)
+{
+ if (clipped_viewport) {
+ while (w->parentWidget() != clipped_viewport) {
+ w = w->parentWidget();
+ if (!w) return 0;
+ }
+ } else {
+ while (w->parentWidget() != viewport) {
+ w = w->parentWidget();
+ if (!w) return 0;
+ }
+ }
+ return rec(w);
+}
+
+void Q3ScrollViewData::hideOrShowAll(Q3ScrollView* sv, bool isScroll)
+{
+ if (!clipped_viewport)
+ return;
+ if (clipped_viewport->x() <= 0
+ && clipped_viewport->y() <= 0
+ && clipped_viewport->width()+clipped_viewport->x() >=
+ viewport->width()
+ && clipped_viewport->height()+clipped_viewport->y() >=
+ viewport->height()) {
+ // clipped_viewport still covers viewport
+ if(static_bg)
+ clipped_viewport->repaint(true);
+ else if ((!isScroll && !clipped_viewport->testAttribute(Qt::WA_StaticContents)) || static_bg)
+ clipped_viewport->update();
+ } else {
+ // Re-center
+ int nx = (viewport->width() - clipped_viewport->width()) / 2;
+ int ny = (viewport->height() - clipped_viewport->height()) / 2;
+ clipped_viewport->move(nx,ny);
+ clipped_viewport->update();
+ }
+ for (QSVChildRec *r = children.first(); r; r=children.next()) {
+ r->hideOrShow(sv, clipped_viewport);
+ }
+}
+
+void Q3ScrollViewData::moveAllBy(int dx, int dy)
+{
+ if (clipped_viewport && !static_bg) {
+ clipped_viewport->move(clipped_viewport->x()+dx,
+ clipped_viewport->y()+dy);
+ } else {
+ for (QSVChildRec *r = children.first(); r; r=children.next()) {
+ r->child->move(r->child->x()+dx,r->child->y()+dy);
+ }
+ if (static_bg)
+ viewport->repaint(true);
+ }
+}
+
+bool Q3ScrollViewData::anyVisibleChildren()
+{
+ for (QSVChildRec *r = children.first(); r; r=children.next()) {
+ if (r->child->isVisible()) return true;
+ }
+ return false;
+}
+
+void Q3ScrollViewData::autoMove(Q3ScrollView* sv)
+{
+ if (policy == Q3ScrollView::AutoOne) {
+ QSVChildRec* r = children.first();
+ if (r)
+ sv->setContentsPos(-r->child->x(),-r->child->y());
+ }
+}
+
+void Q3ScrollViewData::autoResize(Q3ScrollView* sv)
+{
+ if (policy == Q3ScrollView::AutoOne) {
+ QSVChildRec* r = children.first();
+ if (r)
+ sv->resizeContents(r->child->width(),r->child->height());
+ }
+}
+
+void Q3ScrollViewData::autoResizeHint(Q3ScrollView* sv)
+{
+ if (policy == Q3ScrollView::AutoOne) {
+ QSVChildRec* r = children.first();
+ if (r) {
+ QSize s = r->child->sizeHint();
+ if (s.isValid())
+ r->child->resize(s);
+ }
+ } else if (policy == Q3ScrollView::AutoOneFit) {
+ QSVChildRec* r = children.first();
+ if (r) {
+ QSize sh = r->child->sizeHint();
+ sh = sh.boundedTo(r->child->maximumSize());
+ sv->resizeContents(sh.width(), sh.height());
+ }
+ }
+}
+
+void Q3ScrollViewData::viewportResized(int w, int h)
+{
+ if (policy == Q3ScrollView::AutoOneFit) {
+ QSVChildRec* r = children.first();
+ if (r) {
+ QSize sh = r->child->sizeHint();
+ sh = sh.boundedTo(r->child->maximumSize());
+ r->child->resize(QMAX(w,sh.width()), QMAX(h,sh.height()));
+ }
+
+ }
+}
+
+
+/*!
+ \class Q3ScrollView
+ \brief The Q3ScrollView widget provides a scrolling area with on-demand scroll bars.
+
+ \compat
+
+ The Q3ScrollView is a large canvas - potentially larger than the
+ coordinate system normally supported by the underlying window
+ system. This is important because it is quite easy to go beyond
+ these limitations (e.g. many web pages are more than 32000 pixels
+ high). Additionally, the Q3ScrollView can have QWidgets positioned
+ on it that scroll around with the drawn content. These sub-widgets
+ can also have positions outside the normal coordinate range (but
+ they are still limited in size).
+
+ To provide content for the widget, inherit from Q3ScrollView,
+ reimplement drawContents() and use resizeContents() to set the
+ size of the viewed area. Use addChild() and moveChild() to
+ position widgets on the view.
+
+ To use Q3ScrollView effectively it is important to understand its
+ widget structure in the three styles of use: a single large child
+ widget, a large panning area with some widgets and a large panning
+ area with many widgets.
+
+ \section1 Using One Big Widget
+
+ \img qscrollview-vp2.png
+
+ The first, simplest usage of Q3ScrollView (depicted above), is
+ appropriate for scrolling areas that are never more than about
+ 4000 pixels in either dimension (this is about the maximum
+ reliable size on X11 servers). In this usage, you just make one
+ large child in the Q3ScrollView. The child should be a child of the
+ viewport() of the scrollview and be added with addChild():
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 0
+ You can go on to add arbitrary child widgets to the single child
+ in the scrollview as you would with any widget:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 1
+
+ Here the Q3ScrollView has four children: the viewport(), the
+ verticalScrollBar(), the horizontalScrollBar() and a small
+ cornerWidget(). The viewport() has one child: the QWidget. The
+ QWidget has the three QLabel objects as child widgets. When the view
+ is scrolled, the QWidget is moved; its children move with it as
+ child widgets normally do.
+
+ \section1 Using a Very Big View with Some Widgets
+
+ \img qscrollview-vp.png
+
+ The second usage of Q3ScrollView (depicted above) is appropriate
+ when few, if any, widgets are on a very large scrolling area that
+ is potentially larger than 4000 pixels in either dimension. In
+ this usage you call resizeContents() to set the size of the area
+ and reimplement drawContents() to paint the contents. You may also
+ add some widgets by making them children of the viewport() and
+ adding them with addChild() (this is the same as the process for
+ the single large widget in the previous example):
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 2
+ Here, the Q3ScrollView has the same four children: the viewport(),
+ the verticalScrollBar(), the horizontalScrollBar() and a small
+ cornerWidget(). The viewport() has the three QLabel objects as
+ child widgets. When the view is scrolled, the scrollview moves the
+ child widgets individually.
+
+ \section1 Using a Very Big View with Many Widgets
+
+ \img qscrollview-cl.png
+
+ The final usage of Q3ScrollView (depicted above) is appropriate
+ when many widgets are on a very large scrolling area that is
+ potentially larger than 4000 pixels in either dimension. In this
+ usage you call resizeContents() to set the size of the area and
+ reimplement drawContents() to paint the contents. You then call
+ enableClipper(true) and add widgets, again by making them children
+ of the viewport(), and adding them with addChild():
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 3
+
+ Here, the Q3ScrollView has four children: the clipper() (not the
+ viewport() this time), the verticalScrollBar(), the
+ horizontalScrollBar() and a small cornerWidget(). The clipper()
+ has one child: the viewport(). The viewport() has the same three
+ labels as child widgets. When the view is scrolled the viewport()
+ is moved; its children move with it as child widgets normally do.
+
+ \target allviews
+ \section1 Details Relevant for All Views
+
+ Normally you will use the first or third method if you want any
+ child widgets in the view.
+
+ Note that the widget you see in the scrolled area is the
+ viewport() widget, not the Q3ScrollView itself. So to turn mouse
+ tracking on, for example, use viewport()->setMouseTracking(true).
+
+ To enable drag-and-drop, you would setAcceptDrops(true) on the
+ Q3ScrollView (because drag-and-drop events propagate to the
+ parent). But to work out the logical position in the view, you
+ would need to map the drop co-ordinate from being relative to the
+ Q3ScrollView to being relative to the contents; use the function
+ viewportToContents() for this.
+
+ To handle mouse events on the scrolling area, subclass scrollview
+ as you would subclass other widgets, but rather than
+ reimplementing mousePressEvent(), reimplement
+ contentsMousePressEvent() instead. The contents specific event
+ handlers provide translated events in the coordinate system of the
+ scrollview. If you reimplement mousePressEvent(), you'll get
+ called only when part of the Q3ScrollView is clicked: and the only
+ such part is the "corner" (if you don't set a cornerWidget()) and
+ the frame; everything else is covered up by the viewport, clipper
+ or scroll bars.
+
+ When you construct a Q3ScrollView, some of the window flags apply
+ to the viewport() instead of being sent to the QWidget constructor
+ for the Q3ScrollView.
+
+ \list
+
+ \i An image-manipulation widget would use \c
+ WNoAutoErase|WStaticContents because the widget draws all pixels
+ itself, and when its size increases, it only needs a paint event
+ for the new part because the old part remains unchanged.
+
+ \i A scrolling game widget in which the background scrolls as the
+ characters move might use \c WNoAutoErase (in addition to \c
+ WStaticContents) so that the window system background does not
+ flash in and out during scrolling.
+
+ \i A word processing widget might use \c WNoAutoErase and repaint
+ itself line by line to get a less-flickery resizing. If the widget
+ is in a mode in which no text justification can take place, it
+ might use \c WStaticContents too, so that it would only get a
+ repaint for the newly visible parts.
+
+ \endlist
+
+ Child widgets may be moved using addChild() or moveChild(). Use
+ childX() and childY() to get the position of a child widget.
+
+ A widget may be placed in the corner between the vertical and
+ horizontal scroll bars with setCornerWidget(). You can get access
+ to the scroll bars using horizontalScrollBar() and
+ verticalScrollBar(), and to the viewport with viewport(). The
+ scroll view can be scrolled using scrollBy(), ensureVisible(),
+ setContentsPos() or center().
+
+ The visible area is given by visibleWidth() and visibleHeight(),
+ and the contents area by contentsWidth() and contentsHeight(). The
+ contents may be repainted using one of the repaintContents() or
+ updateContents() functions.
+
+ Coordinate conversion is provided by contentsToViewport() and
+ viewportToContents().
+
+ The contentsMoving() signal is emitted just before the contents
+ are moved to a new position.
+
+ \warning Q3ScrollView currently does not erase the background when
+ resized, i.e. you must always clear the background manually in
+ scrollview subclasses. This will change in a future version of Qt
+ and we recommend specifying the \c WNoAutoErase flag explicitly.
+*/
+
+
+/*!
+ \enum Q3ScrollView::ResizePolicy
+
+ This enum type is used to control a Q3ScrollView's reaction to
+ resize events.
+
+ \value Default the Q3ScrollView selects one of the other settings
+ automatically when it has to. In this version of Qt, Q3ScrollView
+ changes to \c Manual if you resize the contents with
+ resizeContents() and to \c AutoOne if a child is added.
+
+ \value Manual the contents stays the size set by resizeContents().
+
+ \value AutoOne if there is only one child widget the contents stays
+ the size of that widget. Otherwise the behavior is undefined.
+
+ \value AutoOneFit if there is only one child widget the contents stays
+ the size of that widget's sizeHint(). If the scrollview is resized
+ larger than the child's sizeHint(), the child will be resized to
+ fit. If there is more than one child, the behavior is undefined.
+
+*/
+//#### The widget will be resized to its sizeHint() when a LayoutHint event
+//#### is received
+
+/*!
+ Constructs a Q3ScrollView called \a name with parent \a parent and
+ widget flags \a f.
+
+ The widget flags \c WStaticContents, \c WNoAutoErase and \c
+ WPaintClever are propagated to the viewport() widget. The other
+ widget flags are propagated to the parent constructor as usual.
+*/
+
+Q3ScrollView::Q3ScrollView(QWidget *parent, const char *name, Qt::WindowFlags f) :
+ Q3Frame(parent, name, f & (~WStaticContents) & (~WNoAutoErase) & (~WResizeNoErase))
+{
+ WindowFlags flags = WResizeNoErase | (f&WPaintClever) | (f&WRepaintNoErase) | (f&WStaticContents);
+ d = new Q3ScrollViewData(this, flags);
+
+#ifndef QT_NO_DRAGANDDROP
+ connect(&d->autoscroll_timer, SIGNAL(timeout()),
+ this, SLOT(doDragAutoScroll()));
+#endif
+
+ connect(d->hbar, SIGNAL(valueChanged(int)),
+ this, SLOT(hslide(int)));
+ connect(d->vbar, SIGNAL(valueChanged(int)),
+ this, SLOT(vslide(int)));
+
+ connect(d->hbar, SIGNAL(sliderPressed()), this, SLOT(hbarIsPressed()));
+ connect(d->hbar, SIGNAL(sliderReleased()), this, SLOT(hbarIsReleased()));
+ connect(d->vbar, SIGNAL(sliderPressed()), this, SLOT(vbarIsPressed()));
+ connect(d->vbar, SIGNAL(sliderReleased()), this, SLOT(vbarIsReleased()));
+
+
+ d->viewport->installEventFilter(this);
+
+ connect(&d->scrollbar_timer, SIGNAL(timeout()),
+ this, SLOT(updateScrollBars()));
+
+ setFrameStyle(Q3Frame::StyledPanel | Q3Frame::Sunken);
+ setLineWidth(style()->pixelMetric(QStyle::PM_DefaultFrameWidth));
+ setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+}
+
+
+/*!
+ Destroys the Q3ScrollView. Any children added with addChild() will
+ be deleted.
+*/
+Q3ScrollView::~Q3ScrollView()
+{
+ // Be careful not to get all those useless events...
+ if (d->clipped_viewport)
+ d->clipped_viewport->removeEventFilter(this);
+ else
+ d->viewport->removeEventFilter(this);
+
+ // order is important
+ // ~QWidget may cause a WM_ERASEBKGND on Windows
+ delete d->vbar;
+ d->vbar = 0;
+ delete d->hbar;
+ d->hbar = 0;
+ delete d->viewport;
+ d->viewport = 0;
+ delete d;
+ d = 0;
+}
+
+/*!
+ \fn void Q3ScrollView::horizontalSliderPressed()
+
+ This signal is emitted whenever the user presses the horizontal slider.
+*/
+/*!
+ \fn void Q3ScrollView::horizontalSliderReleased()
+
+ This signal is emitted whenever the user releases the horizontal slider.
+*/
+/*!
+ \fn void Q3ScrollView::verticalSliderPressed()
+
+ This signal is emitted whenever the user presses the vertical slider.
+*/
+/*!
+ \fn void Q3ScrollView::verticalSliderReleased()
+
+ This signal is emitted whenever the user releases the vertical slider.
+*/
+void Q3ScrollView::hbarIsPressed()
+{
+ d->hbarPressed = true;
+ emit(horizontalSliderPressed());
+}
+
+void Q3ScrollView::hbarIsReleased()
+{
+ d->hbarPressed = false;
+ emit(horizontalSliderReleased());
+}
+
+/*!
+ Returns true if horizontal slider is pressed by user; otherwise returns false.
+*/
+bool Q3ScrollView::isHorizontalSliderPressed()
+{
+ return d->hbarPressed;
+}
+
+void Q3ScrollView::vbarIsPressed()
+{
+ d->vbarPressed = true;
+ emit(verticalSliderPressed());
+}
+
+void Q3ScrollView::vbarIsReleased()
+{
+ d->vbarPressed = false;
+ emit(verticalSliderReleased());
+}
+
+/*!
+ Returns true if vertical slider is pressed by user; otherwise returns false.
+*/
+bool Q3ScrollView::isVerticalSliderPressed()
+{
+ return d->vbarPressed;
+}
+
+/*!
+ \internal
+*/
+void Q3ScrollView::styleChange(QStyle& old)
+{
+ QWidget::styleChange(old);
+ updateScrollBars();
+ d->cachedSizeHint = QSize();
+}
+
+/*!
+ \internal
+*/
+void Q3ScrollView::fontChange(const QFont &old)
+{
+ QWidget::fontChange(old);
+ updateScrollBars();
+ d->cachedSizeHint = QSize();
+}
+
+void Q3ScrollView::hslide(int pos)
+{
+ if (!d->signal_choke) {
+ moveContents(-pos, -d->contentsY());
+ QApplication::syncX();
+ }
+}
+
+void Q3ScrollView::vslide(int pos)
+{
+ if (!d->signal_choke) {
+ moveContents(-d->contentsX(), -pos);
+ QApplication::syncX();
+ }
+}
+
+/*!
+ Called when the horizontal scroll bar geometry changes. This is
+ provided as a protected function so that subclasses can do
+ interesting things such as providing extra buttons in some of the
+ space normally used by the scroll bars.
+
+ The default implementation simply gives all the space to \a hbar.
+ The new geometry is given by \a x, \a y, \a w and \a h.
+
+ \sa setVBarGeometry()
+*/
+void Q3ScrollView::setHBarGeometry(QScrollBar& hbar,
+ int x, int y, int w, int h)
+{
+ hbar.setGeometry(x, y, w, h);
+}
+
+/*!
+ Called when the vertical scroll bar geometry changes. This is
+ provided as a protected function so that subclasses can do
+ interesting things such as providing extra buttons in some of the
+ space normally used by the scroll bars.
+
+ The default implementation simply gives all the space to \a vbar.
+ The new geometry is given by \a x, \a y, \a w and \a h.
+
+ \sa setHBarGeometry()
+*/
+void Q3ScrollView::setVBarGeometry(QScrollBar& vbar,
+ int x, int y, int w, int h)
+{
+ vbar.setGeometry(x, y, w, h);
+}
+
+
+/*!
+ Returns the viewport size for size (\a x, \a y).
+
+ The viewport size depends on (\a x, \a y) (the size of the contents),
+ the size of this widget and the modes of the horizontal and
+ vertical scroll bars.
+
+ This function permits widgets that can trade vertical and
+ horizontal space for each other to control scroll bar appearance
+ better. For example, a word processor or web browser can control
+ the width of the right margin accurately, whether or not there
+ needs to be a vertical scroll bar.
+*/
+
+QSize Q3ScrollView::viewportSize(int x, int y) const
+{
+ int fw = frameWidth();
+ int lmarg = fw+d->l_marg;
+ int rmarg = fw+d->r_marg;
+ int tmarg = fw+d->t_marg;
+ int bmarg = fw+d->b_marg;
+
+ int w = width();
+ int h = height();
+
+ bool needh, needv;
+ bool showh, showv;
+ int hsbExt = horizontalScrollBar()->sizeHint().height();
+ int vsbExt = verticalScrollBar()->sizeHint().width();
+
+ if (d->policy != AutoOne || d->anyVisibleChildren()) {
+ // Do we definitely need the scroll bar?
+ needh = w-lmarg-rmarg < x;
+ needv = h-tmarg-bmarg < y;
+
+ // Do we intend to show the scroll bar?
+ if (d->hMode == AlwaysOn)
+ showh = true;
+ else if (d->hMode == AlwaysOff)
+ showh = false;
+ else
+ showh = needh;
+
+ if (d->vMode == AlwaysOn)
+ showv = true;
+ else if (d->vMode == AlwaysOff)
+ showv = false;
+ else
+ showv = needv;
+
+ // Given other scroll bar will be shown, NOW do we need one?
+ if (showh && h-vsbExt-tmarg-bmarg < y) {
+ if (d->vMode == Auto)
+ showv=true;
+ }
+ if (showv && w-hsbExt-lmarg-rmarg < x) {
+ if (d->hMode == Auto)
+ showh=true;
+ }
+ } else {
+ // Scroll bars not needed, only show scroll bar that are always on.
+ showh = d->hMode == AlwaysOn;
+ showv = d->vMode == AlwaysOn;
+ }
+
+ return QSize(w-lmarg-rmarg - (showv ? vsbExt : 0),
+ h-tmarg-bmarg - (showh ? hsbExt : 0));
+}
+
+
+/*!
+ Updates scroll bars: all possibilities are considered. You should
+ never need to call this in your code.
+*/
+void Q3ScrollView::updateScrollBars()
+{
+ if(!horizontalScrollBar() && !verticalScrollBar())
+ return;
+
+ // I support this should use viewportSize()... but it needs
+ // so many of the temporary variables from viewportSize. hm.
+ int fw = frameWidth();
+ int lmarg = fw+d->l_marg;
+ int rmarg = fw+d->r_marg;
+ int tmarg = fw+d->t_marg;
+ int bmarg = fw+d->b_marg;
+
+ int w = width();
+ int h = height();
+
+ int portw, porth;
+
+ bool needh;
+ bool needv;
+ bool showh;
+ bool showv;
+ bool showc = false;
+
+ int hsbExt = horizontalScrollBar()->sizeHint().height();
+ int vsbExt = verticalScrollBar()->sizeHint().width();
+
+ QSize oldVisibleSize(visibleWidth(), visibleHeight());
+
+ if (d->policy != AutoOne || d->anyVisibleChildren()) {
+ // Do we definitely need the scroll bar?
+ needh = w-lmarg-rmarg < d->contentsWidth();
+ if (d->inresize)
+ needh = !horizontalScrollBar()->isHidden();
+ needv = h-tmarg-bmarg < contentsHeight();
+
+ // Do we intend to show the scroll bar?
+ if (d->hMode == AlwaysOn)
+ showh = true;
+ else if (d->hMode == AlwaysOff)
+ showh = false;
+ else
+ showh = needh;
+
+ if (d->vMode == AlwaysOn)
+ showv = true;
+ else if (d->vMode == AlwaysOff)
+ showv = false;
+ else
+ showv = needv;
+
+#ifdef Q_WS_MAC
+ bool mac_need_scroll = false;
+ if(!parentWidget()) {
+ mac_need_scroll = true;
+ } else {
+ QWidget *tlw = window();
+#ifndef QT_MAC_USE_COCOA
+ QPoint tlw_br = QPoint(tlw->width(), tlw->height()),
+ my_br = qt_mac_posInWindow(this) + QPoint(w, h);
+ if(my_br.x() >= tlw_br.x() - 3 && my_br.y() >= tlw_br.y() - 3)
+#endif
+ mac_need_scroll = true;
+ }
+ if(mac_need_scroll) {
+#ifndef QT_MAC_USE_COCOA
+ WindowAttributes attr;
+ GetWindowAttributes((WindowPtr)handle(), &attr);
+ mac_need_scroll = (attr & kWindowResizableAttribute);
+#endif
+ }
+ if(mac_need_scroll) {
+ showc = true;
+ if(d->vMode == Auto)
+ showv = true;
+ if(d->hMode == Auto)
+ showh = true;
+ }
+#endif
+
+ // Given other scroll bar will be shown, NOW do we need one?
+ if (showh && h-vsbExt-tmarg-bmarg < contentsHeight()) {
+ needv=true;
+ if (d->vMode == Auto)
+ showv=true;
+ }
+ if (showv && !d->inresize && w-hsbExt-lmarg-rmarg < d->contentsWidth()) {
+ needh=true;
+ if (d->hMode == Auto)
+ showh=true;
+ }
+ } else {
+ // Scrollbars not needed, only show scroll bar that are always on.
+ needh = needv = false;
+ showh = d->hMode == AlwaysOn;
+ showv = d->vMode == AlwaysOn;
+ }
+
+ bool sc = d->signal_choke;
+ d->signal_choke=true;
+
+ // Hide unneeded scroll bar, calculate viewport size
+ if (showh) {
+ porth=h-hsbExt-tmarg-bmarg;
+ } else {
+ if (!needh)
+ d->hbar->setValue(0);
+ d->hbar->hide();
+ porth=h-tmarg-bmarg;
+ }
+ if (showv) {
+ portw=w-vsbExt-lmarg-rmarg;
+ } else {
+ if (!needv)
+ d->vbar->setValue(0);
+ d->vbar->hide();
+ portw=w-lmarg-rmarg;
+ }
+
+ // Configure scroll bars that we will show
+ if (needv) {
+ d->vbar->setRange(0, contentsHeight()-porth);
+ d->vbar->setSteps(Q3ScrollView::d->vbar->lineStep(), porth);
+ } else {
+ d->vbar->setRange(0, 0);
+ }
+ if (needh) {
+ d->hbar->setRange(0, QMAX(0, d->contentsWidth()-portw));
+ d->hbar->setSteps(Q3ScrollView::d->hbar->lineStep(), portw);
+ } else {
+ d->hbar->setRange(0, 0);
+ }
+
+ // Position the scroll bars, viewport and corner widget.
+ int bottom;
+ bool reverse = QApplication::reverseLayout();
+ int xoffset = (reverse && (showv || cornerWidget())) ? vsbExt : 0;
+ int xpos = reverse ? 0 : w - vsbExt;
+ bool frameContentsOnly =
+ style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents);
+
+ if(! frameContentsOnly) {
+ if (reverse)
+ xpos += fw;
+ else
+ xpos -= fw;
+ }
+ if (showh) {
+ int right = (showc || showv || cornerWidget()) ? w-vsbExt : w;
+ if (! frameContentsOnly)
+ setHBarGeometry(*d->hbar, fw + xoffset, h-hsbExt-fw,
+ right-fw-fw, hsbExt);
+ else
+ setHBarGeometry(*d->hbar, 0 + xoffset, h-hsbExt, right,
+ hsbExt);
+ bottom=h-hsbExt;
+ } else {
+ bottom=h;
+ }
+ if (showv) {
+ clipper()->setGeometry(lmarg + xoffset, tmarg,
+ w-vsbExt-lmarg-rmarg,
+ bottom-tmarg-bmarg);
+ d->viewportResized(w-vsbExt-lmarg-rmarg, bottom-tmarg-bmarg);
+ if (! frameContentsOnly)
+ changeFrameRect(QRect(0, 0, w, h));
+ else
+ changeFrameRect(QRect(xoffset, 0, w-vsbExt, bottom));
+ if (showc || cornerWidget()) {
+ if (! frameContentsOnly)
+ setVBarGeometry(*d->vbar, xpos,
+ fw, vsbExt,
+ h-hsbExt-fw-fw);
+ else
+ setVBarGeometry(*d->vbar, xpos, 0,
+ vsbExt,
+ h-hsbExt);
+ }
+ else {
+ if (! frameContentsOnly)
+ setVBarGeometry(*d->vbar, xpos,
+ fw, vsbExt,
+ bottom-fw-fw);
+ else
+ setVBarGeometry(*d->vbar, xpos, 0,
+ vsbExt, bottom);
+ }
+ } else {
+ if (! frameContentsOnly)
+ changeFrameRect(QRect(0, 0, w, h));
+ else
+ changeFrameRect(QRect(0, 0, w, bottom));
+ clipper()->setGeometry(lmarg, tmarg,
+ w-lmarg-rmarg, bottom-tmarg-bmarg);
+ d->viewportResized(w-lmarg-rmarg, bottom-tmarg-bmarg);
+ }
+
+ QWidget *corner = d->corner;
+ if (d->corner) {
+ if (! frameContentsOnly)
+ corner->setGeometry(xpos,
+ h-hsbExt-fw,
+ vsbExt,
+ hsbExt);
+ else
+ corner->setGeometry(xpos,
+ h-hsbExt,
+ vsbExt,
+ hsbExt);
+ }
+
+ d->signal_choke=sc;
+
+ if (d->contentsX()+visibleWidth() > d->contentsWidth()) {
+ int x;
+#if 0
+ if (reverse)
+ x =QMIN(0,d->contentsWidth()-visibleWidth());
+ else
+#endif
+ x =QMAX(0,d->contentsWidth()-visibleWidth());
+ d->hbar->setValue(x);
+ // Do it even if it is recursive
+ moveContents(-x, -d->contentsY());
+ }
+ if (d->contentsY()+visibleHeight() > contentsHeight()) {
+ int y=QMAX(0,contentsHeight()-visibleHeight());
+ d->vbar->setValue(y);
+ // Do it even if it is recursive
+ moveContents(-d->contentsX(), -y);
+ }
+
+ // Finally, show the scroll bars
+ if (showh && (d->hbar->isHidden() || !d->hbar->isVisible()))
+ d->hbar->show();
+ if (showv && (d->vbar->isHidden() || !d->vbar->isVisible()))
+ d->vbar->show();
+
+ d->signal_choke=true;
+ d->vbar->setValue(d->contentsY());
+ d->hbar->setValue(d->contentsX());
+ d->signal_choke=false;
+
+ QSize newVisibleSize(visibleWidth(), visibleHeight());
+ if (d->clipped_viewport && oldVisibleSize != newVisibleSize) {
+ QResizeEvent e(newVisibleSize, oldVisibleSize);
+ viewportResizeEvent(&e);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::setVisible(bool visible)
+{
+ if (visible && !isVisible()) {
+ QWidget::setVisible(visible);
+ updateScrollBars();
+ d->hideOrShowAll(this);
+ } else {
+ QWidget::setVisible(visible);
+ }
+}
+
+/*!
+ \internal
+ */
+void Q3ScrollView::resize(int w, int h)
+{
+ QWidget::resize(w, h);
+}
+
+/*!
+ \internal
+*/
+void Q3ScrollView::resize(const QSize& s)
+{
+ resize(s.width(), s.height());
+}
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::resizeEvent(QResizeEvent* event)
+{
+ Q3Frame::resizeEvent(event);
+
+#if 0
+ if (QApplication::reverseLayout()) {
+ d->fake_scroll = true;
+ scrollBy(-event->size().width() + event->oldSize().width(), 0);
+ d->fake_scroll = false;
+ }
+#endif
+
+ bool inresize = d->inresize;
+ d->inresize = true;
+ updateScrollBars();
+ d->inresize = inresize;
+ d->scrollbar_timer.start(0, true);
+
+ d->hideOrShowAll(this);
+}
+
+
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::mousePressEvent(QMouseEvent * e)
+{
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::mouseReleaseEvent(QMouseEvent *e)
+{
+ e->ignore();
+}
+
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::mouseMoveEvent(QMouseEvent *e)
+{
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void Q3ScrollView::wheelEvent(QWheelEvent *e)
+{
+ QWheelEvent ce(viewport()->mapFromGlobal(e->globalPos()),
+ e->globalPos(), e->delta(), e->state());
+ viewportWheelEvent(&ce);
+ if (!ce.isAccepted()) {
+ if (e->orientation() == Horizontal && horizontalScrollBar())
+ horizontalScrollBar()->event(e);
+ else if (e->orientation() == Vertical && verticalScrollBar())
+ verticalScrollBar()->event(e);
+ } else {
+ e->accept();
+ }
+}
+#endif
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::contextMenuEvent(QContextMenuEvent *e)
+{
+ if (e->reason() != QContextMenuEvent::Keyboard) {
+ e->ignore();
+ return;
+ }
+
+ QContextMenuEvent ce(e->reason(), viewport()->mapFromGlobal(e->globalPos()),
+ e->globalPos(), e->state());
+ viewportContextMenuEvent(&ce);
+ if (ce.isAccepted())
+ e->accept();
+ else
+ e->ignore();
+}
+
+Q3ScrollView::ScrollBarMode Q3ScrollView::vScrollBarMode() const
+{
+ return d->vMode;
+}
+
+
+/*!
+ \enum Q3ScrollView::ScrollBarMode
+
+ This enum type describes the various modes of Q3ScrollView's scroll
+ bars.
+
+ \value Auto Q3ScrollView shows a scroll bar when the content is
+ too large to fit and not otherwise. This is the default.
+
+ \value AlwaysOff Q3ScrollView never shows a scroll bar.
+
+ \value AlwaysOn Q3ScrollView always shows a scroll bar.
+
+ (The modes for the horizontal and vertical scroll bars are
+ independent.)
+*/
+
+
+/*!
+ \property Q3ScrollView::vScrollBarMode
+ \brief the mode for the vertical scroll bar
+
+ The default mode is Q3ScrollView::Auto.
+
+ \sa hScrollBarMode
+*/
+void Q3ScrollView::setVScrollBarMode(ScrollBarMode mode)
+{
+ if (d->vMode != mode) {
+ d->vMode = mode;
+ updateScrollBars();
+ }
+}
+
+
+/*!
+ \property Q3ScrollView::hScrollBarMode
+ \brief the mode for the horizontal scroll bar
+
+ The default mode is Q3ScrollView::Auto.
+
+ \sa vScrollBarMode
+*/
+Q3ScrollView::ScrollBarMode Q3ScrollView::hScrollBarMode() const
+{
+ return d->hMode;
+}
+
+void Q3ScrollView::setHScrollBarMode(ScrollBarMode mode)
+{
+ if (d->hMode != mode) {
+ d->hMode = mode;
+ updateScrollBars();
+ }
+}
+
+
+/*!
+ Returns the widget in the corner between the two scroll bars.
+
+ By default, no corner widget is present.
+*/
+QWidget* Q3ScrollView::cornerWidget() const
+{
+ return d->corner;
+}
+
+/*!
+ Sets the widget in the \a corner between the two scroll bars.
+
+ You will probably also want to set at least one of the scroll bar
+ modes to \c AlwaysOn.
+
+ Passing 0 shows no widget in the corner.
+
+ Any previous \a corner widget is hidden.
+
+ You may call setCornerWidget() with the same widget at different
+ times.
+
+ All widgets set here will be deleted by the Q3ScrollView when it is
+ destroyed unless you separately reparent the widget after setting
+ some other corner widget (or 0).
+
+ Any \e newly set widget should have no current parent.
+
+ By default, no corner widget is present.
+
+ \sa setVScrollBarMode(), setHScrollBarMode()
+*/
+void Q3ScrollView::setCornerWidget(QWidget* corner)
+{
+ QWidget* oldcorner = d->corner;
+ if (oldcorner != corner) {
+ if (oldcorner) oldcorner->hide();
+ d->corner = corner;
+ if (corner) corner->setParent(this);
+ updateScrollBars();
+ if (corner) corner->show();
+ }
+}
+
+
+void Q3ScrollView::setResizePolicy(ResizePolicy r)
+{
+ d->policy = r;
+}
+
+/*!
+ \property Q3ScrollView::resizePolicy
+ \brief the resize policy
+
+ The default is \c Default.
+
+ \sa ResizePolicy
+*/
+Q3ScrollView::ResizePolicy Q3ScrollView::resizePolicy() const
+{
+ return d->policy;
+}
+
+/*!
+ \internal
+*/
+void Q3ScrollView::setEnabled(bool enable)
+{
+ Q3Frame::setEnabled(enable);
+}
+
+/*!
+ Removes the \a child widget from the scrolled area. Note that this
+ happens automatically if the \a child is deleted.
+*/
+void Q3ScrollView::removeChild(QWidget* child)
+{
+ if (!d || !child) // First check in case we are destructing
+ return;
+
+ QSVChildRec *r = d->rec(child);
+ if (r) d->deleteChildRec(r);
+}
+
+/*!
+ \internal
+*/
+void Q3ScrollView::removeChild(QObject* child)
+{
+ Q3Frame::removeChild(child);
+}
+
+/*!
+ Inserts the widget, \a child, into the scrolled area positioned at
+ (\a x, \a y). The position defaults to (0, 0). If the child is
+ already in the view, it is just moved.
+
+ You may want to call enableClipper(true) if you add a large number
+ of widgets.
+*/
+void Q3ScrollView::addChild(QWidget* child, int x, int y)
+{
+ if (!child) {
+#if defined(QT_CHECK_NULL)
+ qWarning("Q3ScrollView::addChild(): Cannot add null child");
+#endif
+ return;
+ }
+ child->polish();
+ child->setBackgroundOrigin(WidgetOrigin);
+
+ if (child->parentWidget() == viewport()) {
+ // May already be there
+ QSVChildRec *r = d->rec(child);
+ if (r) {
+ r->moveTo(this,x,y,d->clipped_viewport);
+ if (d->policy > Manual) {
+ d->autoResizeHint(this);
+ d->autoResize(this); // #### better to just deal with this one widget!
+ }
+ return;
+ }
+ }
+
+ if (d->children.isEmpty() && d->policy != Manual) {
+ if (d->policy == Default)
+ setResizePolicy(AutoOne);
+ child->installEventFilter(this);
+ } else if (d->policy == AutoOne) {
+ child->removeEventFilter(this); //#### ?????
+ setResizePolicy(Manual);
+ }
+ if (child->parentWidget() != viewport()) {
+ child->reparent(viewport(), 0, QPoint(0,0), false);
+ }
+ d->addChildRec(child,x,y)->hideOrShow(this, d->clipped_viewport);
+
+ if (d->policy > Manual) {
+ d->autoResizeHint(this);
+ d->autoResize(this); // #### better to just deal with this one widget!
+ }
+}
+
+/*!
+ Repositions the \a child widget to (\a x, \a y). This function is
+ the same as addChild().
+*/
+void Q3ScrollView::moveChild(QWidget* child, int x, int y)
+{
+ addChild(child,x,y);
+}
+
+/*!
+ Returns the X position of the given \a child widget. Use this
+ rather than QWidget::x() for widgets added to the view.
+
+ This function returns 0 if \a child has not been added to the view.
+*/
+int Q3ScrollView::childX(QWidget* child)
+{
+ QSVChildRec *r = d->rec(child);
+ return r ? r->x : 0;
+}
+
+/*!
+ Returns the Y position of the given \a child widget. Use this
+ rather than QWidget::y() for widgets added to the view.
+
+ This function returns 0 if \a child has not been added to the view.
+*/
+int Q3ScrollView::childY(QWidget* child)
+{
+ QSVChildRec *r = d->rec(child);
+ return r ? r->y : 0;
+}
+
+/*! \fn bool Q3ScrollView::childIsVisible(QWidget*)
+ \obsolete
+
+ Returns true if \a child is visible. This is equivalent
+ to child->isVisible().
+*/
+
+/*! \fn void Q3ScrollView::showChild(QWidget* child, bool y)
+ \obsolete
+
+ Sets the visibility of \a child. Equivalent to
+ QWidget::show() or QWidget::hide().
+*/
+
+/*!
+ This event filter ensures the scroll bars are updated when a
+ single contents widget is resized, shown, hidden or destroyed; it
+ passes mouse events to the Q3ScrollView. The event is in \a e and
+ the object is in \a obj.
+*/
+
+bool Q3ScrollView::eventFilter(QObject *obj, QEvent *e)
+{
+ bool disabled = !(qobject_cast<QWidget*>(obj)->isEnabled());
+ if (!d)
+ return false; // we are destructing
+ if (obj == d->viewport || obj == d->clipped_viewport) {
+ switch (e->type()) {
+ /* Forward many events to viewport...() functions */
+ case QEvent::Paint:
+ viewportPaintEvent((QPaintEvent*)e);
+ break;
+ case QEvent::Resize:
+ if (!d->clipped_viewport)
+ viewportResizeEvent((QResizeEvent *)e);
+ break;
+ case QEvent::MouseButtonPress:
+ if (disabled)
+ return false;
+ viewportMousePressEvent((QMouseEvent*)e);
+ if (((QMouseEvent*)e)->isAccepted())
+ return true;
+ break;
+ case QEvent::MouseButtonRelease:
+ if (disabled)
+ return false;
+ viewportMouseReleaseEvent((QMouseEvent*)e);
+ if (((QMouseEvent*)e)->isAccepted())
+ return true;
+ break;
+ case QEvent::MouseButtonDblClick:
+ if (disabled)
+ return false;
+ viewportMouseDoubleClickEvent((QMouseEvent*)e);
+ if (((QMouseEvent*)e)->isAccepted())
+ return true;
+ break;
+ case QEvent::MouseMove:
+ if (disabled)
+ return false;
+ viewportMouseMoveEvent((QMouseEvent*)e);
+ if (((QMouseEvent*)e)->isAccepted())
+ return true;
+ break;
+#ifndef QT_NO_DRAGANDDROP
+ case QEvent::DragEnter:
+ if (disabled)
+ return false;
+ viewportDragEnterEvent((QDragEnterEvent*)e);
+ break;
+ case QEvent::DragMove: {
+ if (disabled)
+ return false;
+ if (d->drag_autoscroll) {
+ QPoint vp = ((QDragMoveEvent*) e)->pos();
+ QRect inside_margin(autoscroll_margin, autoscroll_margin,
+ visibleWidth() - autoscroll_margin * 2,
+ visibleHeight() - autoscroll_margin * 2);
+ if (!inside_margin.contains(vp)) {
+ startDragAutoScroll();
+ // Keep sending move events
+ ((QDragMoveEvent*)e)->accept(QRect(0,0,0,0));
+ }
+ }
+ viewportDragMoveEvent((QDragMoveEvent*)e);
+ } break;
+ case QEvent::DragLeave:
+ if (disabled)
+ return false;
+ stopDragAutoScroll();
+ viewportDragLeaveEvent((QDragLeaveEvent*)e);
+ break;
+ case QEvent::Drop:
+ if (disabled)
+ return false;
+ stopDragAutoScroll();
+ viewportDropEvent((QDropEvent*)e);
+ break;
+#endif // QT_NO_DRAGANDDROP
+#ifndef QT_NO_WHEELEVENT
+ case QEvent::Wheel:
+ if (disabled)
+ return false;
+ break;
+#endif
+ case QEvent::ContextMenu:
+ if (disabled)
+ return false;
+ viewportContextMenuEvent((QContextMenuEvent*)e);
+ if (((QContextMenuEvent*)e)->isAccepted())
+ return true;
+ break;
+ case QEvent::ChildRemoved:
+ removeChild((QWidget*)((QChildEvent*)e)->child());
+ break;
+ case QEvent::LayoutHint:
+ d->autoResizeHint(this);
+ break;
+ default:
+ break;
+ }
+ } else if (d && d->rec((QWidget*)obj)) { // must be a child
+ if (e->type() == QEvent::Resize)
+ d->autoResize(this);
+ else if (e->type() == QEvent::Move)
+ d->autoMove(this);
+ }
+ return Q3Frame::eventFilter(obj, e); // always continue with standard event processing
+}
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ mousePressEvent(): the press position in \a e is translated to be a point
+ on the contents.
+*/
+void Q3ScrollView::contentsMousePressEvent(QMouseEvent* e)
+{
+ e->ignore();
+}
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ mouseReleaseEvent(): the release position in \a e is translated to be a
+ point on the contents.
+*/
+void Q3ScrollView::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ e->ignore();
+}
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ mouseDoubleClickEvent(): the click position in \a e is translated to be a
+ point on the contents.
+
+ The default implementation generates a normal mouse press event.
+*/
+void Q3ScrollView::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+ contentsMousePressEvent(e); // try mouse press event
+}
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ mouseMoveEvent(): the mouse position in \a e is translated to be a point
+ on the contents.
+*/
+void Q3ScrollView::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ e->ignore();
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ dragEnterEvent(): the drag position is translated to be a point
+ on the contents.
+
+ The default implementation does nothing. The \a event parameter is
+ ignored.
+*/
+void Q3ScrollView::contentsDragEnterEvent(QDragEnterEvent * /* event */)
+{
+}
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ dragMoveEvent(): the drag position is translated to be a point on
+ the contents.
+
+ The default implementation does nothing. The \a event parameter is
+ ignored.
+*/
+void Q3ScrollView::contentsDragMoveEvent(QDragMoveEvent * /* event */)
+{
+}
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ dragLeaveEvent(): the drag position is translated to be a point
+ on the contents.
+
+ The default implementation does nothing. The \a event parameter is
+ ignored.
+*/
+void Q3ScrollView::contentsDragLeaveEvent(QDragLeaveEvent * /* event */)
+{
+}
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ dropEvent(): the drop position is translated to be a point on the
+ contents.
+
+ The default implementation does nothing. The \a event parameter is
+ ignored.
+*/
+
+void Q3ScrollView::contentsDropEvent(QDropEvent * /* event */)
+{
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ wheelEvent() in \a{e}: the mouse position is translated to be a
+ point on the contents.
+*/
+#ifndef QT_NO_WHEELEVENT
+void Q3ScrollView::contentsWheelEvent(QWheelEvent * e)
+{
+ e->ignore();
+}
+#endif
+/*!
+ This event handler is called whenever the Q3ScrollView receives a
+ contextMenuEvent() in \a{e}: the mouse position is translated to
+ be a point on the contents.
+*/
+void Q3ScrollView::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+ e->ignore();
+}
+
+/*!
+ This is a low-level painting routine that draws the viewport
+ contents. Reimplement this if drawContents() is too high-level
+ (for example, if you don't want to open a QPainter on the
+ viewport). The paint event is passed in \a pe.
+*/
+void Q3ScrollView::viewportPaintEvent(QPaintEvent* pe)
+{
+ QWidget* vp = viewport();
+
+ QPainter p(vp);
+ QRect r = pe->rect();
+
+ if (d->clipped_viewport) {
+ QRect rr(
+ -d->clipped_viewport->x(), -d->clipped_viewport->y(),
+ d->viewport->width(), d->viewport->height()
+ );
+ r &= rr;
+ if (r.isValid()) {
+ int ex = r.x() + d->clipped_viewport->x() + d->contentsX();
+ int ey = r.y() + d->clipped_viewport->y() + d->contentsY();
+ int ew = r.width();
+ int eh = r.height();
+ drawContentsOffset(&p,
+ d->contentsX()+d->clipped_viewport->x(),
+ d->contentsY()+d->clipped_viewport->y(),
+ ex, ey, ew, eh);
+ }
+ } else {
+ r &= d->viewport->rect();
+ int ex = r.x() + d->contentsX();
+ int ey = r.y() + d->contentsY();
+ int ew = r.width();
+ int eh = r.height();
+ drawContentsOffset(&p, d->contentsX(), d->contentsY(), ex, ey, ew, eh);
+ }
+}
+
+
+/*!
+ To provide simple processing of events on the contents, this
+ function receives all resize events sent to the viewport.
+
+ The default implementation does nothing. The \a event parameter is
+ ignored.
+
+ \sa QWidget::resizeEvent()
+*/
+void Q3ScrollView::viewportResizeEvent(QResizeEvent * /* event */)
+{
+}
+
+/*! \internal
+
+ To provide simple processing of events on the contents, this
+ function receives all mouse press events sent to the viewport,
+ translates the event and calls contentsMousePressEvent().
+
+ \sa contentsMousePressEvent(), QWidget::mousePressEvent()
+*/
+void Q3ScrollView::viewportMousePressEvent(QMouseEvent* e)
+{
+ QMouseEvent ce(e->type(), viewportToContents(e->pos()),
+ e->globalPos(), e->button(), e->state());
+ contentsMousePressEvent(&ce);
+ if (!ce.isAccepted())
+ e->ignore();
+}
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all mouse release events sent to the viewport, translates
+ the event and calls contentsMouseReleaseEvent().
+
+ \sa QWidget::mouseReleaseEvent()
+*/
+void Q3ScrollView::viewportMouseReleaseEvent(QMouseEvent* e)
+{
+ QMouseEvent ce(e->type(), viewportToContents(e->pos()),
+ e->globalPos(), e->button(), e->state());
+ contentsMouseReleaseEvent(&ce);
+ if (!ce.isAccepted())
+ e->ignore();
+}
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all mouse double click events sent to the viewport,
+ translates the event and calls contentsMouseDoubleClickEvent().
+
+ \sa QWidget::mouseDoubleClickEvent()
+*/
+void Q3ScrollView::viewportMouseDoubleClickEvent(QMouseEvent* e)
+{
+ QMouseEvent ce(e->type(), viewportToContents(e->pos()),
+ e->globalPos(), e->button(), e->state());
+ contentsMouseDoubleClickEvent(&ce);
+ if (!ce.isAccepted())
+ e->ignore();
+}
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all mouse move events sent to the viewport, translates the
+ event and calls contentsMouseMoveEvent().
+
+ \sa QWidget::mouseMoveEvent()
+*/
+void Q3ScrollView::viewportMouseMoveEvent(QMouseEvent* e)
+{
+ QMouseEvent ce(e->type(), viewportToContents(e->pos()),
+ e->globalPos(), e->button(), e->state());
+ contentsMouseMoveEvent(&ce);
+ if (!ce.isAccepted())
+ e->ignore();
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all drag enter events sent to the viewport, translates the
+ event and calls contentsDragEnterEvent().
+
+ \sa QWidget::dragEnterEvent()
+*/
+void Q3ScrollView::viewportDragEnterEvent(QDragEnterEvent* e)
+{
+ e->setPoint(viewportToContents(e->pos()));
+ contentsDragEnterEvent(e);
+ e->setPoint(contentsToViewport(e->pos()));
+}
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all drag move events sent to the viewport, translates the
+ event and calls contentsDragMoveEvent().
+
+ \sa QWidget::dragMoveEvent()
+*/
+void Q3ScrollView::viewportDragMoveEvent(QDragMoveEvent* e)
+{
+ e->setPoint(viewportToContents(e->pos()));
+ contentsDragMoveEvent(e);
+ e->setPoint(contentsToViewport(e->pos()));
+}
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all drag leave events sent to the viewport and calls
+ contentsDragLeaveEvent().
+
+ \sa QWidget::dragLeaveEvent()
+*/
+void Q3ScrollView::viewportDragLeaveEvent(QDragLeaveEvent* e)
+{
+ contentsDragLeaveEvent(e);
+}
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all drop events sent to the viewport, translates the event
+ and calls contentsDropEvent().
+
+ \sa QWidget::dropEvent()
+*/
+void Q3ScrollView::viewportDropEvent(QDropEvent* e)
+{
+ e->setPoint(viewportToContents(e->pos()));
+ contentsDropEvent(e);
+ e->setPoint(contentsToViewport(e->pos()));
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!\internal
+
+ To provide simple processing of events on the contents, this function
+ receives all wheel events sent to the viewport, translates the
+ event and calls contentsWheelEvent().
+
+ \sa QWidget::wheelEvent()
+*/
+#ifndef QT_NO_WHEELEVENT
+void Q3ScrollView::viewportWheelEvent(QWheelEvent* e)
+{
+ /*
+ Different than standard mouse events, because wheel events might
+ be sent to the focus widget if the widget-under-mouse doesn't want
+ the event itself.
+ */
+ QWheelEvent ce(viewportToContents(e->pos()),
+ e->globalPos(), e->delta(), e->state());
+ contentsWheelEvent(&ce);
+ if (ce.isAccepted())
+ e->accept();
+ else
+ e->ignore();
+}
+#endif
+
+/*! \internal
+
+ To provide simple processing of events on the contents, this function
+ receives all context menu events sent to the viewport, translates the
+ event and calls contentsContextMenuEvent().
+*/
+void Q3ScrollView::viewportContextMenuEvent(QContextMenuEvent *e)
+{
+ QContextMenuEvent ce(e->reason(), viewportToContents(e->pos()), e->globalPos(), e->state());
+ contentsContextMenuEvent(&ce);
+ if (ce.isAccepted())
+ e->accept();
+ else
+ e->ignore();
+}
+
+/*!
+ Returns the component horizontal scroll bar. It is made available
+ to allow accelerators, autoscrolling, etc.
+
+ It should not be used for other purposes.
+
+ This function never returns 0.
+*/
+QScrollBar* Q3ScrollView::horizontalScrollBar() const
+{
+ return d->hbar;
+}
+
+/*!
+ Returns the component vertical scroll bar. It is made available to
+ allow accelerators, autoscrolling, etc.
+
+ It should not be used for other purposes.
+
+ This function never returns 0.
+*/
+QScrollBar* Q3ScrollView::verticalScrollBar() const {
+ return d->vbar;
+}
+
+
+/*!
+ Scrolls the content so that the point (\a x, \a y) is visible with at
+ least 50-pixel margins (if possible, otherwise centered).
+*/
+void Q3ScrollView::ensureVisible(int x, int y)
+{
+ ensureVisible(x, y, 50, 50);
+}
+
+/*!
+ \overload
+
+ Scrolls the content so that the point (\a x, \a y) is visible with at
+ least the \a xmargin and \a ymargin margins (if possible,
+ otherwise centered).
+*/
+void Q3ScrollView::ensureVisible(int x, int y, int xmargin, int ymargin)
+{
+ int pw=visibleWidth();
+ int ph=visibleHeight();
+
+ int cx=-d->contentsX();
+ int cy=-d->contentsY();
+ int cw=d->contentsWidth();
+ int ch=contentsHeight();
+
+ if (pw < xmargin*2)
+ xmargin=pw/2;
+ if (ph < ymargin*2)
+ ymargin=ph/2;
+
+ if (cw <= pw) {
+ xmargin=0;
+ cx=0;
+ }
+ if (ch <= ph) {
+ ymargin=0;
+ cy=0;
+ }
+
+ if (x < -cx+xmargin)
+ cx = -x+xmargin;
+ else if (x >= -cx+pw-xmargin)
+ cx = -x+pw-xmargin;
+
+ if (y < -cy+ymargin)
+ cy = -y+ymargin;
+ else if (y >= -cy+ph-ymargin)
+ cy = -y+ph-ymargin;
+
+ if (cx > 0)
+ cx=0;
+ else if (cx < pw-cw && cw>pw)
+ cx=pw-cw;
+
+ if (cy > 0)
+ cy=0;
+ else if (cy < ph-ch && ch>ph)
+ cy=ph-ch;
+
+ setContentsPos(-cx, -cy);
+}
+
+/*!
+ Scrolls the content so that the point (\a x, \a y) is in the top-left
+ corner.
+*/
+void Q3ScrollView::setContentsPos(int x, int y)
+{
+#if 0
+ // bounds checking...
+ if (QApplication::reverseLayout())
+ if (x > d->contentsWidth() - visibleWidth()) x = d->contentsWidth() - visibleWidth();
+ else
+#endif
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+ // Choke signal handling while we update BOTH sliders.
+ d->signal_choke=true;
+ moveContents(-x, -y);
+ d->vbar->setValue(y);
+ d->hbar->setValue(x);
+ d->signal_choke=false;
+}
+
+/*!
+ Scrolls the content by \a dx to the left and \a dy upwards.
+*/
+void Q3ScrollView::scrollBy(int dx, int dy)
+{
+ setContentsPos(QMAX(d->contentsX()+dx, 0), QMAX(d->contentsY()+dy, 0));
+}
+
+/*!
+ Scrolls the content so that the point (\a x, \a y) is in the center
+ of visible area.
+*/
+void Q3ScrollView::center(int x, int y)
+{
+ ensureVisible(x, y, 32000, 32000);
+}
+
+/*!
+ \overload
+
+ Scrolls the content so that the point (\a x, \a y) is visible with
+ the \a xmargin and \a ymargin margins (as fractions of visible
+ the area).
+
+ For example:
+ \list
+ \i Margin 0.0 allows (x, y) to be on the edge of the visible area.
+ \i Margin 0.5 ensures that (x, y) is in middle 50% of the visible area.
+ \i Margin 1.0 ensures that (x, y) is in the center of the visible area.
+ \endlist
+*/
+void Q3ScrollView::center(int x, int y, float xmargin, float ymargin)
+{
+ int pw=visibleWidth();
+ int ph=visibleHeight();
+ ensureVisible(x, y, int(xmargin/2.0*pw+0.5), int(ymargin/2.0*ph+0.5));
+}
+
+
+/*!
+ \fn void Q3ScrollView::contentsMoving(int x, int y)
+
+ This signal is emitted just before the contents are moved to
+ position (\a x, \a y).
+
+ \sa contentsX(), contentsY()
+*/
+
+/*!
+ Moves the contents by (\a x, \a y).
+*/
+void Q3ScrollView::moveContents(int x, int y)
+{
+ if (-x+visibleWidth() > d->contentsWidth())
+#if 0
+ if(QApplication::reverseLayout())
+ x=QMAX(0,-d->contentsWidth()+visibleWidth());
+ else
+#endif
+ x=QMIN(0,-d->contentsWidth()+visibleWidth());
+ if (-y+visibleHeight() > contentsHeight())
+ y=QMIN(0,-contentsHeight()+visibleHeight());
+
+ int dx = x - d->vx;
+ int dy = y - d->vy;
+
+ if (!dx && !dy)
+ return; // Nothing to do
+
+ emit contentsMoving(-x, -y);
+
+ d->vx = x;
+ d->vy = y;
+
+ if (d->clipped_viewport || d->static_bg) {
+ // Cheap move (usually)
+ d->moveAllBy(dx,dy);
+ } else if (/*dx && dy ||*/
+ (QABS(dy) * 5 > visibleHeight() * 4) ||
+ (QABS(dx) * 5 > visibleWidth() * 4)
+ )
+ {
+ // Big move
+ if (viewport()->updatesEnabled())
+ viewport()->update();
+ d->moveAllBy(dx,dy);
+ } else if (!d->fake_scroll || d->contentsWidth() > visibleWidth()) {
+ // Small move
+ clipper()->scroll(dx,dy);
+ }
+ d->hideOrShowAll(this, true);
+}
+
+/*!
+ \property Q3ScrollView::contentsX
+ \brief the X coordinate of the contents that are at the left edge of
+ the viewport.
+*/
+int Q3ScrollView::contentsX() const
+{
+ return d->contentsX();
+}
+
+/*!
+ \property Q3ScrollView::contentsY
+ \brief the Y coordinate of the contents that are at the top edge of
+ the viewport.
+*/
+int Q3ScrollView::contentsY() const
+{
+ return d->contentsY();
+}
+
+/*!
+ \property Q3ScrollView::contentsWidth
+ \brief the width of the contents area
+*/
+int Q3ScrollView::contentsWidth() const
+{
+ return d->contentsWidth();
+}
+
+/*!
+ \property Q3ScrollView::contentsHeight
+ \brief the height of the contents area
+*/
+int Q3ScrollView::contentsHeight() const
+{
+ return d->vheight;
+}
+
+/*!
+ Sets the size of the contents area to \a w pixels wide and \a h
+ pixels high and updates the viewport accordingly.
+*/
+void Q3ScrollView::resizeContents(int w, int h)
+{
+ int ow = d->vwidth;
+ int oh = d->vheight;
+ d->vwidth = w;
+ d->vheight = h;
+
+ d->scrollbar_timer.start(0, true);
+
+ if (d->children.isEmpty() && d->policy == Default)
+ setResizePolicy(Manual);
+
+ if (ow > w) {
+ // Swap
+ int t=w;
+ w=ow;
+ ow=t;
+ }
+ // Refresh area ow..w
+ if (ow < visibleWidth() && w >= 0) {
+ if (ow < 0)
+ ow = 0;
+ if (w > visibleWidth())
+ w = visibleWidth();
+ clipper()->update(d->contentsX()+ow, 0, w-ow, visibleHeight());
+ }
+
+ if (oh > h) {
+ // Swap
+ int t=h;
+ h=oh;
+ oh=t;
+ }
+ // Refresh area oh..h
+ if (oh < visibleHeight() && h >= 0) {
+ if (oh < 0)
+ oh = 0;
+ if (h > visibleHeight())
+ h = visibleHeight();
+ clipper()->update(0, d->contentsY()+oh, visibleWidth(), h-oh);
+ }
+}
+
+/*!
+ Calls update() on a rectangle defined by \a x, \a y, \a w, \a h,
+ translated appropriately. If the rectangle is not visible, nothing
+ is repainted.
+
+ \sa repaintContents()
+*/
+void Q3ScrollView::updateContents(int x, int y, int w, int h)
+{
+ if (!isVisible() || !updatesEnabled())
+ return;
+
+ QWidget* vp = viewport();
+
+ // Translate
+ x -= d->contentsX();
+ y -= d->contentsY();
+
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
+
+ if (w < 0 || h < 0)
+ return;
+ if (x > visibleWidth() || y > visibleHeight())
+ return;
+
+ if (w > visibleWidth())
+ w = visibleWidth();
+ if (h > visibleHeight())
+ h = visibleHeight();
+
+ if (d->clipped_viewport) {
+ // Translate clipper() to viewport()
+ x -= d->clipped_viewport->x();
+ y -= d->clipped_viewport->y();
+ }
+
+ vp->update(x, y, w, h);
+}
+
+/*!
+ \overload
+
+ Updates the contents in rectangle \a r
+*/
+void Q3ScrollView::updateContents(const QRect& r)
+{
+ updateContents(r.x(), r.y(), r.width(), r.height());
+}
+
+/*!
+ \overload
+*/
+void Q3ScrollView::updateContents()
+{
+ updateContents(d->contentsX(), d->contentsY(), visibleWidth(), visibleHeight());
+}
+
+/*!
+ \overload
+
+ Repaints the contents of rectangle \a r. If \a erase is true the
+ background is cleared using the background color.
+*/
+void Q3ScrollView::repaintContents(const QRect& r, bool erase)
+{
+ repaintContents(r.x(), r.y(), r.width(), r.height(), erase);
+}
+
+
+/*!
+ \overload
+
+ Repaints the contents. If \a erase is true the background is
+ cleared using the background color.
+*/
+void Q3ScrollView::repaintContents(bool erase)
+{
+ repaintContents(d->contentsX(), d->contentsY(), visibleWidth(), visibleHeight(), erase);
+}
+
+
+/*!
+ Calls repaint() on a rectangle defined by \a x, \a y, \a w, \a h,
+ translated appropriately. If the rectangle is not visible, nothing
+ is repainted. If \a erase is true the background is cleared using
+ the background color.
+
+ \sa updateContents()
+*/
+void Q3ScrollView::repaintContents(int x, int y, int w, int h, bool /*erase*/)
+{
+ if (!isVisible() || !updatesEnabled())
+ return;
+
+ QWidget* vp = viewport();
+
+ // Translate logical to clipper()
+ x -= d->contentsX();
+ y -= d->contentsY();
+
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
+
+ if (w < 0 || h < 0)
+ return;
+ if (w > visibleWidth())
+ w = visibleWidth();
+ if (h > visibleHeight())
+ h = visibleHeight();
+
+ if (d->clipped_viewport) {
+ // Translate clipper() to viewport()
+ x -= d->clipped_viewport->x();
+ y -= d->clipped_viewport->y();
+ }
+
+ vp->update(x, y, w, h);
+}
+
+
+/*!
+ For backward-compatibility only. It is easier to use
+ drawContents(QPainter*,int,int,int,int).
+
+ The default implementation translates the painter appropriately
+ and calls drawContents(QPainter*,int,int,int,int). See
+ drawContents() for an explanation of the parameters \a p, \a
+ offsetx, \a offsety, \a clipx, \a clipy, \a clipw and \a cliph.
+*/
+void Q3ScrollView::drawContentsOffset(QPainter* p, int offsetx, int offsety, int clipx, int clipy, int clipw, int cliph)
+{
+ p->translate(-offsetx,-offsety);
+ drawContents(p, clipx, clipy, clipw, cliph);
+}
+
+/*!
+ \fn void Q3ScrollView::drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph)
+
+ Reimplement this function if you are viewing a drawing area rather
+ than a widget.
+
+ The function should draw the rectangle (\a clipx, \a clipy, \a
+ clipw, \a cliph) of the contents using painter \a p. The clip
+ rectangle is in the scrollview's coordinates.
+
+ For example:
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 4
+
+ The clip rectangle and translation of the painter \a p is already
+ set appropriately.
+*/
+void Q3ScrollView::drawContents(QPainter*, int, int, int, int)
+{
+}
+
+
+/*!
+ \reimp
+*/
+void Q3ScrollView::frameChanged()
+{
+ // slight ugle-hack - the listview header needs readjusting when
+ // changing the frame
+ if (Q3ListView *lv = qobject_cast<Q3ListView *>(this))
+ lv->triggerUpdate();
+ Q3Frame::frameChanged();
+ updateScrollBars();
+}
+
+
+/*!
+ Returns the viewport widget of the scrollview. This is the widget
+ containing the contents widget or which is the drawing area.
+*/
+QWidget* Q3ScrollView::viewport() const
+{
+ if (d->clipped_viewport)
+ return d->clipped_viewport;
+ return d->viewport;
+}
+
+/*!
+ Returns the clipper widget. Contents in the scrollview are
+ ultimately clipped to be inside the clipper widget.
+
+ You should not need to use this function.
+
+ \sa visibleWidth(), visibleHeight()
+*/
+QWidget* Q3ScrollView::clipper() const
+{
+ return d->viewport;
+}
+
+/*!
+ \property Q3ScrollView::visibleWidth
+ \brief the horizontal amount of the content that is visible
+*/
+int Q3ScrollView::visibleWidth() const
+{
+ return clipper()->width();
+}
+
+/*!
+ \property Q3ScrollView::visibleHeight
+ \brief the vertical amount of the content that is visible
+*/
+int Q3ScrollView::visibleHeight() const
+{
+ return clipper()->height();
+}
+
+
+void Q3ScrollView::changeFrameRect(const QRect& r)
+{
+ QRect oldr = frameRect();
+ if (oldr != r) {
+ QRect cr = contentsRect();
+ QRegion fr(frameRect());
+ fr = fr.subtracted(contentsRect());
+ setFrameRect(r);
+ if (isVisible()) {
+ cr = cr.intersected(contentsRect());
+ fr = fr.united(frameRect());
+ fr = fr.subtracted(cr);
+ if (!fr.isEmpty())
+ update(fr);
+ }
+ }
+}
+
+
+/*!
+ Sets the margins around the scrolling area to \a left, \a top, \a
+ right and \a bottom. This is useful for applications such as
+ spreadsheets with "locked" rows and columns. The marginal space is
+ \e inside the frameRect() and is left blank; reimplement
+ drawFrame() or put widgets in the unused area.
+
+ By default all margins are zero.
+
+ \sa frameChanged()
+*/
+void Q3ScrollView::setMargins(int left, int top, int right, int bottom)
+{
+ if (left == d->l_marg &&
+ top == d->t_marg &&
+ right == d->r_marg &&
+ bottom == d->b_marg)
+ return;
+
+ d->l_marg = left;
+ d->t_marg = top;
+ d->r_marg = right;
+ d->b_marg = bottom;
+ updateScrollBars();
+}
+
+
+/*!
+ Returns the left margin.
+
+ \sa setMargins()
+*/
+int Q3ScrollView::leftMargin() const
+{
+ return d->l_marg;
+}
+
+
+/*!
+ Returns the top margin.
+
+ \sa setMargins()
+*/
+int Q3ScrollView::topMargin() const
+{
+ return d->t_marg;
+}
+
+
+/*!
+ Returns the right margin.
+
+ \sa setMargins()
+*/
+int Q3ScrollView::rightMargin() const
+{
+ return d->r_marg;
+}
+
+
+/*!
+ Returns the bottom margin.
+
+ \sa setMargins()
+*/
+int Q3ScrollView::bottomMargin() const
+{
+ return d->b_marg;
+}
+
+/*!
+ \reimp
+*/
+bool Q3ScrollView::focusNextPrevChild(bool next)
+{
+ // Makes sure that the new focus widget is on-screen, if
+ // necessary by scrolling the scroll view.
+ bool retval = Q3Frame::focusNextPrevChild(next);
+ if (retval) {
+ QWidget *w = window()->focusWidget();
+ if (isAncestorOf(w)) {
+ QSVChildRec *r = d->ancestorRec(w);
+ if (r && (r->child == w || w->isVisibleTo(r->child))) {
+ QPoint cp = r->child->mapToGlobal(QPoint(0, 0));
+ QPoint cr = w->mapToGlobal(QPoint(0, 0)) - cp;
+ ensureVisible(r->x + cr.x() + w->width()/2, r->y + cr.y() + w->height()/2,
+ w->width()/2, w->height()/2);
+ }
+ }
+ }
+ return retval;
+}
+
+
+
+/*!
+ When a large numbers of child widgets are in a scrollview,
+ especially if they are close together, the scrolling performance
+ can suffer greatly. If \a y is true the scrollview will use an
+ extra widget to group child widgets.
+
+ Note that you may only call enableClipper() prior to adding
+ widgets.
+*/
+void Q3ScrollView::enableClipper(bool y)
+{
+ if (!d->clipped_viewport == !y)
+ return;
+ if (d->children.count())
+ qFatal("May only call Q3ScrollView::enableClipper() before adding widgets");
+ if (y) {
+ d->clipped_viewport = new QClipperWidget(clipper(), "qt_clipped_viewport", QFlag(d->flags));
+ d->clipped_viewport->setGeometry(-coord_limit/2,-coord_limit/2,
+ coord_limit,coord_limit);
+ d->clipped_viewport->setBackgroundMode(d->viewport->backgroundMode());
+ d->viewport->setBackgroundMode(NoBackground); // no exposures for this
+ d->viewport->removeEventFilter(this);
+ d->clipped_viewport->installEventFilter(this);
+ d->clipped_viewport->show();
+ } else {
+ delete d->clipped_viewport;
+ d->clipped_viewport = 0;
+ }
+}
+
+/*!
+ Sets the scrollview to have a static background if \a y is true,
+ or a scrolling background if \a y is false. By default, the
+ background is scrolling.
+
+ Be aware that this mode is quite slow, as a full repaint of the
+ visible area has to be triggered on every contents move.
+
+ \sa hasStaticBackground()
+*/
+void Q3ScrollView::setStaticBackground(bool y)
+{
+ d->static_bg = y;
+}
+
+/*!
+ Returns true if Q3ScrollView uses a static background; otherwise
+ returns false.
+
+ \sa setStaticBackground()
+*/
+bool Q3ScrollView::hasStaticBackground() const
+{
+ return d->static_bg;
+}
+
+/*!
+ \overload
+
+ Returns the point \a p translated to a point on the viewport()
+ widget.
+*/
+QPoint Q3ScrollView::contentsToViewport(const QPoint& p) const
+{
+ if (d->clipped_viewport) {
+ return QPoint(p.x() - d->contentsX() - d->clipped_viewport->x(),
+ p.y() - d->contentsY() - d->clipped_viewport->y());
+ } else {
+ return QPoint(p.x() - d->contentsX(),
+ p.y() - d->contentsY());
+ }
+}
+
+/*!
+ \overload
+
+ Returns the point on the viewport \a vp translated to a point in
+ the contents.
+*/
+QPoint Q3ScrollView::viewportToContents(const QPoint& vp) const
+{
+ if (d->clipped_viewport) {
+ return QPoint(vp.x() + d->contentsX() + d->clipped_viewport->x(),
+ vp.y() + d->contentsY() + d->clipped_viewport->y());
+ } else {
+ return QPoint(vp.x() + d->contentsX(),
+ vp.y() + d->contentsY());
+ }
+}
+
+
+/*!
+ Translates a point (\a x, \a y) in the contents to a point (\a vx,
+ \a vy) on the viewport() widget.
+*/
+void Q3ScrollView::contentsToViewport(int x, int y, int& vx, int& vy) const
+{
+ const QPoint v = contentsToViewport(QPoint(x,y));
+ vx = v.x();
+ vy = v.y();
+}
+
+/*!
+ Translates a point (\a vx, \a vy) on the viewport() widget to a
+ point (\a x, \a y) in the contents.
+*/
+void Q3ScrollView::viewportToContents(int vx, int vy, int& x, int& y) const
+{
+ const QPoint c = viewportToContents(QPoint(vx,vy));
+ x = c.x();
+ y = c.y();
+}
+
+/*!
+ \reimp
+*/
+QSize Q3ScrollView::sizeHint() const
+{
+ if (d->use_cached_size_hint && d->cachedSizeHint.isValid())
+ return d->cachedSizeHint;
+
+ constPolish();
+ int f = 2 * frameWidth();
+ int h = fontMetrics().height();
+ QSize sz(f, f);
+ if (d->policy > Manual) {
+ QSVChildRec *r = d->children.first();
+ if (r) {
+ QSize cs = r->child->sizeHint();
+ if (cs.isValid())
+ sz += cs.boundedTo(r->child->maximumSize());
+ else
+ sz += r->child->size();
+ }
+ } else {
+ sz += QSize(d->contentsWidth(), contentsHeight());
+ }
+ if (d->vMode == AlwaysOn)
+ sz.setWidth(sz.width() + d->vbar->sizeHint().width());
+ if (d->hMode == AlwaysOn)
+ sz.setHeight(sz.height() + d->hbar->sizeHint().height());
+ return sz.expandedTo(QSize(12 * h, 8 * h))
+ .boundedTo(QSize(36 * h, 24 * h));
+}
+
+
+/*!
+ \reimp
+*/
+QSize Q3ScrollView::minimumSizeHint() const
+{
+ int h = fontMetrics().height();
+ if (h < 10)
+ h = 10;
+ int f = 2 * frameWidth();
+ return QSize((6 * h) + f, (4 * h) + f);
+}
+
+
+/*!
+ \reimp
+
+ (Implemented to get rid of a compiler warning.)
+*/
+void Q3ScrollView::drawContents(QPainter *)
+{
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ \internal
+*/
+void Q3ScrollView::startDragAutoScroll()
+{
+ if (!d->autoscroll_timer.isActive()) {
+ d->autoscroll_time = initialScrollTime;
+ d->autoscroll_accel = initialScrollAccel;
+ d->autoscroll_timer.start(d->autoscroll_time);
+ }
+}
+
+
+/*!
+ \internal
+*/
+void Q3ScrollView::stopDragAutoScroll()
+{
+ d->autoscroll_timer.stop();
+}
+
+
+/*!
+ \internal
+*/
+void Q3ScrollView::doDragAutoScroll()
+{
+ QPoint p = d->viewport->mapFromGlobal(QCursor::pos());
+
+ if (d->autoscroll_accel-- <= 0 && d->autoscroll_time) {
+ d->autoscroll_accel = initialScrollAccel;
+ d->autoscroll_time--;
+ d->autoscroll_timer.start(d->autoscroll_time);
+ }
+ int l = QMAX(1, (initialScrollTime- d->autoscroll_time));
+
+ int dx = 0, dy = 0;
+ if (p.y() < autoscroll_margin) {
+ dy = -l;
+ } else if (p.y() > visibleHeight() - autoscroll_margin) {
+ dy = +l;
+ }
+ if (p.x() < autoscroll_margin) {
+ dx = -l;
+ } else if (p.x() > visibleWidth() - autoscroll_margin) {
+ dx = +l;
+ }
+ if (dx || dy) {
+ scrollBy(dx,dy);
+ } else {
+ stopDragAutoScroll();
+ }
+}
+
+
+/*!
+ \property Q3ScrollView::dragAutoScroll
+ \brief whether autoscrolling in drag move events is enabled
+
+ If this property is set to true (the default), the Q3ScrollView
+ automatically scrolls the contents in drag move events if the user
+ moves the cursor close to a border of the view. Of course this
+ works only if the viewport accepts drops. Specifying false
+ disables this autoscroll feature.
+*/
+
+void Q3ScrollView::setDragAutoScroll(bool b)
+{
+ d->drag_autoscroll = b;
+}
+
+bool Q3ScrollView::dragAutoScroll() const
+{
+ return d->drag_autoscroll;
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*!\internal
+ */
+void Q3ScrollView::setCachedSizeHint(const QSize &sh) const
+{
+ if (isVisible() && !d->cachedSizeHint.isValid())
+ d->cachedSizeHint = sh;
+}
+
+/*!\internal
+ */
+void Q3ScrollView::disableSizeHintCaching()
+{
+ d->use_cached_size_hint = false;
+}
+
+/*!\internal
+ */
+QSize Q3ScrollView::cachedSizeHint() const
+{
+ return d->use_cached_size_hint ? d->cachedSizeHint : QSize();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SCROLLVIEW
diff --git a/src/qt3support/widgets/q3scrollview.h b/src/qt3support/widgets/q3scrollview.h
new file mode 100644
index 0000000..8579dbb
--- /dev/null
+++ b/src/qt3support/widgets/q3scrollview.h
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SCROLLVIEW_H
+#define Q3SCROLLVIEW_H
+
+#include <Qt3Support/q3frame.h>
+#include <QtGui/qscrollbar.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3ScrollViewData;
+
+class Q_COMPAT_EXPORT Q3ScrollView : public Q3Frame
+{
+ Q_OBJECT
+ Q_ENUMS( ResizePolicy ScrollBarMode )
+ Q_PROPERTY( ResizePolicy resizePolicy READ resizePolicy WRITE setResizePolicy )
+ Q_PROPERTY( ScrollBarMode vScrollBarMode READ vScrollBarMode WRITE setVScrollBarMode )
+ Q_PROPERTY( ScrollBarMode hScrollBarMode READ hScrollBarMode WRITE setHScrollBarMode )
+ Q_PROPERTY( int visibleWidth READ visibleWidth )
+ Q_PROPERTY( int visibleHeight READ visibleHeight )
+ Q_PROPERTY( int contentsWidth READ contentsWidth )
+ Q_PROPERTY( int contentsHeight READ contentsHeight )
+ Q_PROPERTY( int contentsX READ contentsX )
+ Q_PROPERTY( int contentsY READ contentsY )
+ Q_PROPERTY( bool dragAutoScroll READ dragAutoScroll WRITE setDragAutoScroll )
+
+public:
+ Q3ScrollView(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0);
+ ~Q3ScrollView();
+
+ enum ResizePolicy { Default, Manual, AutoOne, AutoOneFit };
+ virtual void setResizePolicy( ResizePolicy );
+ ResizePolicy resizePolicy() const;
+
+ void styleChange( QStyle & );
+ void removeChild(QWidget* child);
+ virtual void addChild( QWidget* child, int x=0, int y=0 );
+ virtual void moveChild( QWidget* child, int x, int y );
+ int childX(QWidget* child);
+ int childY(QWidget* child);
+ bool childIsVisible(QWidget* child) { return child->isVisible(); } // obsolete functions
+ void showChild(QWidget* child, bool yes=true) { child->setVisible(yes); }
+
+ enum ScrollBarMode { Auto, AlwaysOff, AlwaysOn };
+
+ ScrollBarMode vScrollBarMode() const;
+ virtual void setVScrollBarMode( ScrollBarMode );
+
+ ScrollBarMode hScrollBarMode() const;
+ virtual void setHScrollBarMode( ScrollBarMode );
+
+ QWidget* cornerWidget() const;
+ virtual void setCornerWidget(QWidget*);
+
+ // ### 4.0: Consider providing a factory function for scrollbars
+ // (e.g. make the two following functions virtual)
+ QScrollBar* horizontalScrollBar() const;
+ QScrollBar* verticalScrollBar() const;
+ QWidget* viewport() const;
+ QWidget* clipper() const;
+
+ int visibleWidth() const;
+ int visibleHeight() const;
+
+ int contentsWidth() const;
+ int contentsHeight() const;
+ int contentsX() const;
+ int contentsY() const;
+
+ void resize( int w, int h );
+ void resize( const QSize& );
+ void setVisible(bool visible);
+
+ void updateContents( int x, int y, int w, int h );
+ void updateContents( const QRect& r );
+ void updateContents();
+ void repaintContents( int x, int y, int w, int h, bool erase=true );
+ void repaintContents( const QRect& r, bool erase=true );
+ void repaintContents( bool erase=true );
+ void contentsToViewport( int x, int y, int& vx, int& vy ) const;
+ void viewportToContents( int vx, int vy, int& x, int& y ) const;
+ QPoint contentsToViewport( const QPoint& ) const;
+ QPoint viewportToContents( const QPoint& ) const;
+ void enableClipper( bool y );
+
+ void setStaticBackground( bool y );
+ bool hasStaticBackground() const;
+
+ QSize viewportSize( int, int ) const;
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ void removeChild(QObject* child);
+
+ bool isHorizontalSliderPressed();
+ bool isVerticalSliderPressed();
+
+ virtual void setDragAutoScroll( bool b );
+ bool dragAutoScroll() const;
+
+Q_SIGNALS:
+ void contentsMoving(int x, int y);
+ void horizontalSliderPressed();
+ void horizontalSliderReleased();
+ void verticalSliderPressed();
+ void verticalSliderReleased();
+
+public Q_SLOTS:
+ virtual void resizeContents( int w, int h );
+ void scrollBy( int dx, int dy );
+ virtual void setContentsPos( int x, int y );
+ void ensureVisible(int x, int y);
+ void ensureVisible(int x, int y, int xmargin, int ymargin);
+ void center(int x, int y);
+ void center(int x, int y, float xmargin, float ymargin);
+
+ void updateScrollBars(); // ### virtual in 4.0
+ void setEnabled( bool enable );
+
+protected:
+ virtual void drawContents(QPainter*, int cx, int cy, int cw, int ch);
+ virtual void drawContentsOffset(QPainter*, int ox, int oy,
+ int cx, int cy, int cw, int ch);
+
+
+ virtual void contentsMousePressEvent( QMouseEvent* );
+ virtual void contentsMouseReleaseEvent( QMouseEvent* );
+ virtual void contentsMouseDoubleClickEvent( QMouseEvent* );
+ virtual void contentsMouseMoveEvent( QMouseEvent* );
+ virtual void contentsDragEnterEvent( QDragEnterEvent * );
+ virtual void contentsDragMoveEvent( QDragMoveEvent * );
+ virtual void contentsDragLeaveEvent( QDragLeaveEvent * );
+ virtual void contentsDropEvent( QDropEvent * );
+ virtual void contentsWheelEvent( QWheelEvent * );
+ virtual void contentsContextMenuEvent( QContextMenuEvent * );
+
+
+ virtual void viewportPaintEvent( QPaintEvent* );
+ virtual void viewportResizeEvent( QResizeEvent* );
+ virtual void viewportMousePressEvent( QMouseEvent* );
+ virtual void viewportMouseReleaseEvent( QMouseEvent* );
+ virtual void viewportMouseDoubleClickEvent( QMouseEvent* );
+ virtual void viewportMouseMoveEvent( QMouseEvent* );
+ virtual void viewportDragEnterEvent( QDragEnterEvent * );
+ virtual void viewportDragMoveEvent( QDragMoveEvent * );
+ virtual void viewportDragLeaveEvent( QDragLeaveEvent * );
+ virtual void viewportDropEvent( QDropEvent * );
+ virtual void viewportWheelEvent( QWheelEvent * );
+ virtual void viewportContextMenuEvent( QContextMenuEvent * );
+
+ void frameChanged();
+
+public:
+ virtual void setMargins(int left, int top, int right, int bottom);
+ int leftMargin() const;
+ int topMargin() const;
+ int rightMargin() const;
+ int bottomMargin() const;
+protected:
+
+ bool focusNextPrevChild( bool next );
+
+ virtual void setHBarGeometry(QScrollBar& hbar, int x, int y, int w, int h);
+ virtual void setVBarGeometry(QScrollBar& vbar, int x, int y, int w, int h);
+
+ void resizeEvent(QResizeEvent*);
+ void mousePressEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+ void mouseDoubleClickEvent( QMouseEvent * );
+ void mouseMoveEvent( QMouseEvent * );
+ void wheelEvent( QWheelEvent * );
+ void contextMenuEvent( QContextMenuEvent * );
+ bool eventFilter( QObject *, QEvent *e );
+
+ void setCachedSizeHint( const QSize &sh ) const;
+ QSize cachedSizeHint() const;
+ void fontChange( const QFont & );
+
+private:
+ void drawContents( QPainter* );
+ void moveContents(int x, int y);
+
+ Q3ScrollViewData* d;
+
+private Q_SLOTS:
+ void hslide(int);
+ void vslide(int);
+ void hbarIsPressed();
+ void hbarIsReleased();
+ void vbarIsPressed();
+ void vbarIsReleased();
+ void doDragAutoScroll();
+ void startDragAutoScroll();
+ void stopDragAutoScroll();
+
+private: // Disabled copy constructor and operator=
+ Q_DISABLE_COPY(Q3ScrollView)
+ void changeFrameRect(const QRect&);
+
+public:
+ void disableSizeHintCaching();
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SCROLLVIEW_H
diff --git a/src/qt3support/widgets/q3spinwidget.cpp b/src/qt3support/widgets/q3spinwidget.cpp
new file mode 100644
index 0000000..32ac981
--- /dev/null
+++ b/src/qt3support/widgets/q3spinwidget.cpp
@@ -0,0 +1,475 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3rangecontrol.h"
+
+#ifndef QT_NO_SPINWIDGET
+
+#include "qabstractspinbox.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qrect.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SpinWidgetPrivate
+{
+public:
+ Q3SpinWidgetPrivate()
+ : upEnabled(true),
+ downEnabled(true),
+ theButton(0),
+ buttonDown(0),
+ timerUp(0),
+ bsyms(Q3SpinWidget::UpDownArrows),
+ ed (0) {}
+ uint upEnabled :1;
+ uint downEnabled :1;
+ uint theButton :2;
+ uint buttonDown :2;
+ uint timerUp : 1;
+ QRect up;
+ QRect down;
+ QTimer auRepTimer;
+ Q3SpinWidget::ButtonSymbols bsyms;
+ QWidget *ed;
+ void startTimer(int msec) { auRepTimer.start(msec, true); }
+ void startTimer(bool up, int msec) { timerUp = up; startTimer(msec); }
+ void stopTimer() { auRepTimer.stop(); }
+};
+
+/*!
+ \class Q3SpinWidget
+ \brief The Q3SpinWidget class is an internal range control related class.
+
+ \internal
+
+ Constructs an empty range control widget with parent \a parent
+ called \a name.
+
+*/
+
+Q3SpinWidget::Q3SpinWidget(QWidget* parent, const char* name)
+ : QWidget(parent, name)
+{
+ d = new Q3SpinWidgetPrivate();
+ connect(&d->auRepTimer, SIGNAL(timeout()), this, SLOT(timerDone()));
+ setFocusPolicy(Qt::StrongFocus);
+
+ arrange();
+ updateDisplay();
+}
+
+
+/*! Destroys the object and frees any allocated resources.
+
+*/
+
+Q3SpinWidget::~Q3SpinWidget()
+{
+ delete d;
+}
+
+/*! */
+QWidget * Q3SpinWidget::editWidget()
+{
+ return d->ed;
+}
+
+/*!
+ Sets the editing widget to \a w.
+*/
+void Q3SpinWidget::setEditWidget(QWidget * w)
+{
+ if (w) {
+ if (w->parentWidget() != this)
+ w->setParent(this);
+ setFocusProxy(w);
+ }
+ d->ed = w;
+ arrange();
+ updateDisplay();
+}
+
+/*! \reimp
+
+*/
+
+void Q3SpinWidget::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() != Qt::LeftButton) {
+ d->stopTimer();
+ d->buttonDown = 0;
+ d->theButton = 0;
+ repaint(d->down.united(d->up));
+ return;
+ }
+
+ uint oldButtonDown = d->buttonDown;
+
+ if (d->down.contains(e->pos()) && d->downEnabled)
+ d->buttonDown = 1;
+ else if (d->up.contains(e->pos()) && d->upEnabled)
+ d->buttonDown = 2;
+ else
+ d->buttonDown = 0;
+
+ d->theButton = d->buttonDown;
+ if (oldButtonDown != d->buttonDown) {
+ if (!d->buttonDown) {
+ repaint(d->down.united(d->up));
+ } else if (d->buttonDown & 1) {
+ repaint(d->down);
+ stepDown();
+ d->startTimer(false, 300);
+ } else if (d->buttonDown & 2) {
+ repaint(d->up);
+ stepUp();
+ d->startTimer(true, 300);
+ }
+ }
+
+ if (!oldButtonDown && !d->buttonDown)
+ e->ignore();
+
+}
+
+static QStyleOptionSpinBox getStyleOption(const Q3SpinWidget *spin)
+{
+ QStyleOptionSpinBox opt;
+ opt.init(spin);
+ opt.frame = true;
+ opt.subControls = 0;
+ opt.buttonSymbols = (QAbstractSpinBox::ButtonSymbols)spin->buttonSymbols();
+ opt.stepEnabled = 0;
+ if (spin->isUpEnabled())
+ opt.stepEnabled |= QAbstractSpinBox::StepUpEnabled;
+ if (spin->isDownEnabled())
+ opt.stepEnabled |= QAbstractSpinBox::StepDownEnabled;
+ opt.activeSubControls = 0;
+ return opt;
+}
+
+/*!
+
+*/
+
+void Q3SpinWidget::arrange()
+{
+ QStyleOptionSpinBox opt = getStyleOption(this);
+ d->up = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxUp, this);
+ d->down = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxDown, this);
+ if (d->ed) {
+ QRect r = style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this);
+ d->ed->setGeometry(r);
+ }
+}
+
+/*!
+
+*/
+
+void Q3SpinWidget::stepUp()
+{
+ emit stepUpPressed();
+}
+
+void Q3SpinWidget::resizeEvent(QResizeEvent*)
+{
+ arrange();
+}
+
+/*!
+
+*/
+
+void Q3SpinWidget::stepDown()
+{
+ emit stepDownPressed();
+}
+
+
+void Q3SpinWidget::timerDone()
+{
+ // we use a double timer to make it possible for users to do
+ // something with 0-timer on valueChanged.
+ QTimer::singleShot(1, this, SLOT(timerDoneEx()));
+}
+
+void Q3SpinWidget::timerDoneEx()
+{
+ if (!d->buttonDown)
+ return;
+ if (d->timerUp)
+ stepUp();
+ else
+ stepDown();
+ d->startTimer(100);
+}
+
+
+/*!
+ The event is passed in \a e.
+*/
+
+void Q3SpinWidget::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() != Qt::LeftButton)
+ return;
+
+ uint oldButtonDown = d->theButton;
+ d->theButton = 0;
+ if (oldButtonDown != d->theButton) {
+ if (oldButtonDown & 1)
+ repaint(d->down);
+ else if (oldButtonDown & 2)
+ repaint(d->up);
+ }
+ d->stopTimer();
+ d->buttonDown = 0;
+
+ if (!oldButtonDown && !d->buttonDown)
+ e->ignore();
+}
+
+
+/*!
+ The event is passed in \a e.
+*/
+
+void Q3SpinWidget::mouseMoveEvent(QMouseEvent *e)
+{
+ if (!(e->state() & Qt::LeftButton))
+ return;
+
+ uint oldButtonDown = d->theButton;
+ if (oldButtonDown & 1 && !d->down.contains(e->pos())) {
+ d->stopTimer();
+ d->theButton = 0;
+ repaint(d->down);
+ } else if (oldButtonDown & 2 && !d->up.contains(e->pos())) {
+ d->stopTimer();
+ d->theButton = 0;
+ repaint(d->up);
+ } else if (!oldButtonDown && d->up.contains(e->pos()) && d->buttonDown & 2) {
+ d->startTimer(500);
+ d->theButton = 2;
+ repaint(d->up);
+ } else if (!oldButtonDown && d->down.contains(e->pos()) && d->buttonDown & 1) {
+ d->startTimer(500);
+ d->theButton = 1;
+ repaint(d->down);
+ }
+
+ if (!oldButtonDown && !d->buttonDown)
+ e->ignore();
+}
+
+
+/*!
+ The event is passed in \a e.
+*/
+#ifndef QT_NO_WHEELEVENT
+void Q3SpinWidget::wheelEvent(QWheelEvent *e)
+{
+ e->accept();
+ static float offset = 0;
+ static Q3SpinWidget* offset_owner = 0;
+ if (offset_owner != this) {
+ offset_owner = this;
+ offset = 0;
+ }
+ offset += -e->delta()/120;
+ if (QABS(offset) < 1)
+ return;
+ int ioff = int(offset);
+ int i;
+ for(i=0; i < QABS(ioff); i++)
+ offset > 0 ? stepDown() : stepUp();
+ offset -= ioff;
+}
+#endif
+
+/*!
+
+*/
+void Q3SpinWidget::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOptionSpinBox opt = getStyleOption(this);
+
+ if (d->theButton & 1) {
+ opt.activeSubControls = QStyle::SC_SpinBoxDown;
+ opt.state |= QStyle::State_Sunken;
+ } else if (d->theButton & 2) {
+ opt.activeSubControls = QStyle::SC_SpinBoxUp;
+ opt.state |= QStyle::State_Sunken;
+ } else
+ opt.activeSubControls = QStyle::SC_None;
+ opt.rect = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxFrame, this);
+ opt.subControls = QStyle::SC_All;
+ style()->drawComplexControl(QStyle::CC_SpinBox, &opt, &p, this);
+}
+
+
+// ### What does the QEvent passed in contain? It used to be the previous style.
+/*!
+ The previous style is passed in \a ev.
+*/
+void Q3SpinWidget::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange) {
+ arrange();
+ } else if(ev->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow() && d->buttonDown) { //was active, but lost focus
+ d->stopTimer();
+ d->buttonDown = 0;
+ d->theButton = 0;
+ }
+ } else if(ev->type() == QEvent::EnabledChange) {
+ d->upEnabled = isEnabled();
+ d->downEnabled = isEnabled();
+ updateDisplay();
+ }
+ QWidget::changeEvent(ev);
+}
+
+/*!
+*/
+
+QRect Q3SpinWidget::upRect() const
+{
+ return d->up;
+}
+
+/*!
+*/
+
+QRect Q3SpinWidget::downRect() const
+{
+ return d->down;
+}
+
+/*!
+*/
+
+void Q3SpinWidget::updateDisplay()
+{
+ if (!isEnabled()) {
+ d->upEnabled = false;
+ d->downEnabled = false;
+ }
+ if (d->theButton & 1 && (d->downEnabled) == 0) {
+ d->theButton &= ~1;
+ d->buttonDown &= ~1;
+ }
+
+ if (d->theButton & 2 && (d->upEnabled) == 0) {
+ d->theButton &= ~2;
+ d->buttonDown &= ~2;
+ }
+ repaint();
+}
+
+/*!
+ Sets up-enabled to \a on.
+*/
+
+void Q3SpinWidget::setUpEnabled(bool on)
+{
+ if ((bool)d->upEnabled != on) {
+ d->upEnabled = on;
+ updateDisplay();
+ }
+}
+
+/*!
+*/
+
+bool Q3SpinWidget::isUpEnabled() const
+{
+ return d->upEnabled;
+}
+
+/*!
+ Sets down-enabled to \a on.
+*/
+
+void Q3SpinWidget::setDownEnabled(bool on)
+{
+ if ((bool)d->downEnabled != on) {
+ d->downEnabled = on;
+ updateDisplay();
+ }
+}
+
+/*!
+*/
+
+bool Q3SpinWidget::isDownEnabled() const
+{
+ return d->downEnabled;
+}
+
+/*!
+ Sets the button symbol to \a bs.
+*/
+
+void Q3SpinWidget::setButtonSymbols(ButtonSymbols bs)
+{
+ d->bsyms = bs;
+}
+
+/*!
+*/
+
+Q3SpinWidget::ButtonSymbols Q3SpinWidget::buttonSymbols() const
+{
+ return d->bsyms;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/widgets/q3titlebar.cpp b/src/qt3support/widgets/q3titlebar.cpp
new file mode 100644
index 0000000..61b7c12
--- /dev/null
+++ b/src/qt3support/widgets/q3titlebar.cpp
@@ -0,0 +1,630 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#ifndef QT_NO_TITLEBAR
+
+#include "qapplication.h"
+#include "qcursor.h"
+#include "qdatetime.h"
+#include "qevent.h"
+#include "qimage.h"
+#include "qpainter.h"
+#include "qiodevice.h"
+#include "qpixmap.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "qtooltip.h"
+#include "qdebug.h"
+#if defined(Q_WS_WIN)
+#include "qt_windows.h"
+#endif
+
+#include "private/qapplication_p.h"
+#include "private/q3titlebar_p.h"
+#include "private/qwidget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3TitleBarPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(Q3TitleBar)
+public:
+ Q3TitleBarPrivate()
+ : toolTip(0), act(0), window(0), movable(1), pressed(0), autoraise(0), inevent(0)
+ {
+ }
+
+ Qt::WindowFlags flags;
+ QStyle::SubControl buttonDown;
+ QPoint moveOffset;
+ QToolTip *toolTip;
+ bool act :1;
+ QWidget* window;
+ bool movable :1;
+ bool pressed :1;
+ bool autoraise :1;
+ bool inevent : 1;
+
+ int titleBarState() const;
+ QStyleOptionTitleBar getStyleOption() const;
+ void readColors();
+};
+
+inline int Q3TitleBarPrivate::titleBarState() const
+{
+ uint state = window ? window->windowState() : static_cast<Qt::WindowStates>(Qt::WindowNoState);
+ state |= uint(act ? QStyle::State_Active : QStyle::State_None);
+ return (int)state;
+}
+
+QStyleOptionTitleBar Q3TitleBarPrivate::getStyleOption() const
+{
+ Q_Q(const Q3TitleBar);
+ QStyleOptionTitleBar opt;
+ opt.init(q);
+ opt.text = q->windowTitle();
+ //################
+ QIcon icon = q->windowIcon();
+ QSize s = icon.actualSize(QSize(64, 64));
+ opt.icon = icon.pixmap(s);
+ opt.subControls = QStyle::SC_All;
+ opt.activeSubControls = QStyle::SC_None;
+ opt.titleBarState = titleBarState();
+ opt.titleBarFlags = flags;
+ return opt;
+}
+
+Q3TitleBar::Q3TitleBar(QWidget *w, QWidget *parent, Qt::WindowFlags f)
+ : QWidget(*new Q3TitleBarPrivate, parent, Qt::WStyle_Customize | Qt::WStyle_NoBorder)
+{
+ Q_D(Q3TitleBar);
+ if (f == 0 && w)
+ f = w->windowFlags();
+ d->flags = f;
+ d->window = w;
+ d->buttonDown = QStyle::SC_None;
+ d->act = 0;
+ if (w) {
+ if (w->minimumSize() == w->maximumSize())
+ d->flags &= ~Qt::WindowMaximizeButtonHint;
+ setWindowTitle(w->windowTitle());
+ }
+
+ d->readColors();
+ setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+ setMouseTracking(true);
+ setAutoRaise(style()->styleHint(QStyle::SH_TitleBar_AutoRaise, 0, this));
+}
+
+void Q3TitleBar::setFakeWindowFlags(Qt::WindowFlags f)
+{
+ Q_D(Q3TitleBar);
+ d->flags = f;
+}
+
+Qt::WindowFlags Q3TitleBar::fakeWindowFlags() const
+{
+ Q_D(const Q3TitleBar);
+ return d->flags;
+}
+
+Q3TitleBar::~Q3TitleBar()
+{
+}
+
+QStyleOptionTitleBar Q3TitleBar::getStyleOption() const
+{
+ return d_func()->getStyleOption();
+}
+
+#ifdef Q_WS_WIN
+static inline QRgb colorref2qrgb(COLORREF col)
+{
+ return qRgb(GetRValue(col),GetGValue(col),GetBValue(col));
+}
+#endif
+
+void Q3TitleBarPrivate::readColors()
+{
+ Q_Q(Q3TitleBar);
+ QPalette pal = q->palette();
+
+ bool colorsInitialized = false;
+
+#ifdef Q_WS_WIN // ask system properties on windows
+ if (QApplication::desktopSettingsAware()) {
+ pal.setColor(QPalette::Active, QPalette::Highlight, colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION)));
+ pal.setColor(QPalette::Inactive, QPalette::Highlight, colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION)));
+ pal.setColor(QPalette::Active, QPalette::HighlightedText, colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT)));
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT)));
+ colorsInitialized = true;
+ BOOL gradient = false;
+ SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradient, 0);
+ if (gradient) {
+ pal.setColor(QPalette::Active, QPalette::Base, colorref2qrgb(GetSysColor(COLOR_GRADIENTACTIVECAPTION)));
+ pal.setColor(QPalette::Inactive, QPalette::Base, colorref2qrgb(GetSysColor(COLOR_GRADIENTINACTIVECAPTION)));
+ } else {
+ pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Active, QPalette::Highlight));
+ pal.setColor(QPalette::Inactive, QPalette::Base, pal.color(QPalette::Inactive, QPalette::Highlight));
+ }
+ }
+#endif // Q_WS_WIN
+ if (!colorsInitialized) {
+ pal.setColor(QPalette::Active, QPalette::Highlight,
+ pal.color(QPalette::Active, QPalette::Highlight));
+ pal.setColor(QPalette::Active, QPalette::Base,
+ pal.color(QPalette::Active, QPalette::Highlight));
+ pal.setColor(QPalette::Inactive, QPalette::Highlight,
+ pal.color(QPalette::Inactive, QPalette::Dark));
+ pal.setColor(QPalette::Inactive, QPalette::Base,
+ pal.color(QPalette::Inactive, QPalette::Dark));
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ pal.color(QPalette::Inactive, QPalette::Window));
+ }
+
+ q->setPalette(pal);
+ q->setActive(act);
+}
+
+void Q3TitleBar::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::ModifiedChange)
+ update();
+ QWidget::changeEvent(ev);
+}
+
+void Q3TitleBar::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(Q3TitleBar);
+ if (!d->act)
+ emit doActivate();
+ if (e->button() == Qt::LeftButton) {
+ d->pressed = true;
+ QStyleOptionTitleBar opt = d->getStyleOption();
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt,
+ e->pos(), this);
+ switch (ctrl) {
+ case QStyle::SC_TitleBarSysMenu:
+ if (d->flags & Qt::WindowSystemMenuHint) {
+ d->buttonDown = QStyle::SC_None;
+ static QTime *t = 0;
+ static Q3TitleBar *tc = 0;
+ if (!t)
+ t = new QTime;
+ if (tc != this || t->elapsed() > QApplication::doubleClickInterval()) {
+ emit showOperationMenu();
+ t->start();
+ tc = this;
+ } else {
+ tc = 0;
+ emit doClose();
+ return;
+ }
+ }
+ break;
+
+ case QStyle::SC_TitleBarShadeButton:
+ case QStyle::SC_TitleBarUnshadeButton:
+ if (d->flags & Qt::WindowShadeButtonHint)
+ d->buttonDown = ctrl;
+ break;
+
+ case QStyle::SC_TitleBarNormalButton:
+ if (d->flags & Qt::WindowMinMaxButtonsHint)
+ d->buttonDown = ctrl;
+ break;
+
+ case QStyle::SC_TitleBarMinButton:
+ if (d->flags & Qt::WindowMinimizeButtonHint)
+ d->buttonDown = ctrl;
+ break;
+
+ case QStyle::SC_TitleBarMaxButton:
+ if (d->flags & Qt::WindowMaximizeButtonHint)
+ d->buttonDown = ctrl;
+ break;
+
+ case QStyle::SC_TitleBarCloseButton:
+ if (d->flags & Qt::WindowSystemMenuHint)
+ d->buttonDown = ctrl;
+ break;
+
+ case QStyle::SC_TitleBarLabel:
+ d->buttonDown = ctrl;
+ d->moveOffset = mapToParent(e->pos());
+ break;
+
+ default:
+ break;
+ }
+ repaint();
+ } else {
+ d->pressed = false;
+ }
+}
+
+void Q3TitleBar::contextMenuEvent(QContextMenuEvent *e)
+{
+ Q_D(Q3TitleBar);
+ QStyleOptionTitleBar opt = d->getStyleOption();
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, e->pos(),
+ this);
+ if(ctrl == QStyle::SC_TitleBarLabel || ctrl == QStyle::SC_TitleBarSysMenu) {
+ e->accept();
+ emit popupOperationMenu(e->globalPos());
+ } else {
+ e->ignore();
+ }
+}
+
+void Q3TitleBar::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(Q3TitleBar);
+ if (e->button() == Qt::LeftButton && d->pressed) {
+ e->accept();
+ QStyleOptionTitleBar opt = d->getStyleOption();
+ QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt,
+ e->pos(), this);
+ d->pressed = false;
+ if (ctrl == d->buttonDown) {
+ d->buttonDown = QStyle::SC_None;
+ repaint();
+ switch(ctrl) {
+ case QStyle::SC_TitleBarShadeButton:
+ case QStyle::SC_TitleBarUnshadeButton:
+ if(d->flags & Qt::WindowShadeButtonHint)
+ emit doShade();
+ break;
+
+ case QStyle::SC_TitleBarNormalButton:
+ if(d->flags & Qt::WindowMaximizeButtonHint)
+ emit doNormal();
+ break;
+
+ case QStyle::SC_TitleBarMinButton:
+ if(d->flags & Qt::WindowMinimizeButtonHint) {
+ if (d->window && d->window->isMinimized())
+ emit doNormal();
+ else
+ emit doMinimize();
+ }
+ break;
+
+ case QStyle::SC_TitleBarMaxButton:
+ if(d->flags & Qt::WindowMaximizeButtonHint) {
+ if(d->window && d->window->isMaximized())
+ emit doNormal();
+ else
+ emit doMaximize();
+ }
+ break;
+
+ case QStyle::SC_TitleBarCloseButton:
+ if(d->flags & Qt::WindowSystemMenuHint) {
+ d->buttonDown = QStyle::SC_None;
+ repaint();
+ emit doClose();
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ e->ignore();
+ }
+}
+
+void Q3TitleBar::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(Q3TitleBar);
+ e->accept();
+ switch (d->buttonDown) {
+ case QStyle::SC_None:
+ if(autoRaise())
+ repaint();
+ break;
+ case QStyle::SC_TitleBarSysMenu:
+ break;
+ case QStyle::SC_TitleBarShadeButton:
+ case QStyle::SC_TitleBarUnshadeButton:
+ case QStyle::SC_TitleBarNormalButton:
+ case QStyle::SC_TitleBarMinButton:
+ case QStyle::SC_TitleBarMaxButton:
+ case QStyle::SC_TitleBarCloseButton:
+ {
+ QStyle::SubControl last_ctrl = d->buttonDown;
+ QStyleOptionTitleBar opt = d->getStyleOption();
+ d->buttonDown = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, e->pos(), this);
+ if (d->buttonDown != last_ctrl)
+ d->buttonDown = QStyle::SC_None;
+ repaint();
+ d->buttonDown = last_ctrl;
+ }
+ break;
+
+ case QStyle::SC_TitleBarLabel:
+ if (d->buttonDown == QStyle::SC_TitleBarLabel && d->movable && d->pressed) {
+ if ((d->moveOffset - mapToParent(e->pos())).manhattanLength() >= 4) {
+ QPoint p = mapFromGlobal(e->globalPos());
+
+ QWidget *parent = d->window ? d->window->parentWidget() : 0;
+ if(parent && parent->inherits("Q3WorkspaceChild")) {
+ QWidget *workspace = parent->parentWidget();
+ p = workspace->mapFromGlobal(e->globalPos());
+ if (!workspace->rect().contains(p)) {
+ if (p.x() < 0)
+ p.rx() = 0;
+ if (p.y() < 0)
+ p.ry() = 0;
+ if (p.x() > workspace->width())
+ p.rx() = workspace->width();
+ if (p.y() > workspace->height())
+ p.ry() = workspace->height();
+ }
+ }
+
+ QPoint pp = p - d->moveOffset;
+ if (!parentWidget()->isMaximized())
+ parentWidget()->move(pp);
+ }
+ } else {
+ QStyle::SubControl last_ctrl = d->buttonDown;
+ d->buttonDown = QStyle::SC_None;
+ if(d->buttonDown != last_ctrl)
+ repaint();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void Q3TitleBar::resizeEvent(QResizeEvent *r)
+{
+ QWidget::resizeEvent(r);
+ cutText();
+}
+
+bool Q3TitleBar::isTool() const
+{
+ return (d_func()->flags & Qt::WindowType_Mask) == Qt::Tool;
+}
+
+void Q3TitleBar::paintEvent(QPaintEvent *)
+{
+ Q_D(Q3TitleBar);
+ QStyleOptionTitleBar opt = d->getStyleOption();
+ opt.subControls = QStyle::SC_TitleBarLabel;
+ opt.activeSubControls = d->buttonDown;
+ if (d->flags & Qt::WindowSystemMenuHint) {
+ opt.subControls |= QStyle::SC_TitleBarSysMenu | QStyle::SC_TitleBarCloseButton;
+ if (d->window && (d->flags & Qt::WindowShadeButtonHint)) {
+ if (d->window->isMinimized())
+ opt.subControls |= QStyle::SC_TitleBarUnshadeButton;
+ else
+ opt.subControls |= QStyle::SC_TitleBarShadeButton;
+ }
+ if (d->window && (d->flags & Qt::WindowMinMaxButtonsHint)) {
+ if(d->window && d->window->isMinimized())
+ opt.subControls |= QStyle::SC_TitleBarNormalButton;
+ else
+ opt.subControls |= QStyle::SC_TitleBarMinButton;
+ }
+ if (d->window && (d->flags & Qt::WindowMaximizeButtonHint) && !d->window->isMaximized())
+ opt.subControls |= QStyle::SC_TitleBarMaxButton;
+ }
+ QStyle::SubControl under_mouse = QStyle::SC_None;
+
+ if (underMouse()) {
+ under_mouse = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt,
+ mapFromGlobal(QCursor::pos()), this);
+ opt.activeSubControls |= under_mouse;
+ if (d->pressed)
+ opt.state |= QStyle::State_Sunken;
+ else if(autoRaise())
+ opt.state |= QStyle::State_MouseOver;
+ }
+
+ opt.palette.setCurrentColorGroup(usesActiveColor() ? QPalette::Active : QPalette::Inactive);
+
+ QPainter p(this);
+ if (!windowTitle().isEmpty())
+ opt.titleBarFlags |= Qt::WindowTitleHint;
+ style()->drawComplexControl(QStyle::CC_TitleBar, &opt, &p, this);
+}
+
+void Q3TitleBar::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(Q3TitleBar);
+ if (e->button() != Qt::LeftButton) {
+ e->ignore();
+ return;
+ }
+ e->accept();
+ QStyleOptionTitleBar opt = d->getStyleOption();
+ switch (style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, e->pos(), this)) {
+ case QStyle::SC_TitleBarLabel:
+ emit doubleClicked();
+ break;
+
+ case QStyle::SC_TitleBarSysMenu:
+ if (d->flags & Qt::WStyle_SysMenu)
+ emit doClose();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void Q3TitleBar::cutText()
+{
+ Q_D(Q3TitleBar);
+ QFontMetrics fm(font());
+ QStyleOptionTitleBar opt = d->getStyleOption();
+ int maxw = style()->subControlRect(QStyle::CC_TitleBar, &opt, QStyle::SC_TitleBarLabel,
+ this).width();
+ if (!d->window)
+ return;
+
+ QString txt = d->window->windowTitle();
+ if (style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, 0, this) && d->window
+ && d->window->isWindowModified())
+ txt += QLatin1String(" *");
+
+ QString cuttext = txt;
+ if (fm.width(txt + QLatin1Char('m')) > maxw) {
+ int i = txt.length();
+ int dotlength = fm.width(QLatin1String("..."));
+ while (i>0 && fm.width(txt.left(i)) + dotlength > maxw)
+ i--;
+ if(i != (int)txt.length())
+ cuttext = txt.left(i) + QLatin1String("...");
+ }
+
+ setWindowTitle(cuttext);
+}
+
+
+void Q3TitleBar::leaveEvent(QEvent *)
+{
+ if(autoRaise() && !d_func()->pressed)
+ repaint();
+}
+
+void Q3TitleBar::enterEvent(QEvent *)
+{
+ if(autoRaise() && !d_func()->pressed)
+ repaint();
+ QEvent e(QEvent::Leave);
+ QApplication::sendEvent(parentWidget(), &e);
+}
+
+void Q3TitleBar::setActive(bool active)
+{
+ Q_D(Q3TitleBar);
+ if (d->act == active)
+ return ;
+
+ d->act = active;
+ update();
+}
+
+bool Q3TitleBar::isActive() const
+{
+ return d_func()->act;
+}
+
+bool Q3TitleBar::usesActiveColor() const
+{
+ return (isActive() && isActiveWindow()) ||
+ (!window() && QWidget::window()->isActiveWindow());
+}
+
+QWidget *Q3TitleBar::window() const
+{
+ return d_func()->window;
+}
+
+bool Q3TitleBar::event(QEvent *e)
+{
+ Q_D(Q3TitleBar);
+ if (d->inevent)
+ return QWidget::event(e);
+ d->inevent = true;
+ if (e->type() == QEvent::ApplicationPaletteChange) {
+ d->readColors();
+ return true;
+ } else if (e->type() == QEvent::WindowActivate) {
+ setActive(d->act);
+ } else if (e->type() == QEvent::WindowDeactivate) {
+ bool wasActive = d->act;
+ setActive(false);
+ d->act = wasActive;
+ } else if (e->type() == QEvent::WindowIconChange) {
+ update();
+ } else if (e->type() == QEvent::WindowTitleChange) {
+ cutText();
+ update();
+ }
+
+ d->inevent = false;
+ return QWidget::event(e);
+}
+
+void Q3TitleBar::setMovable(bool b)
+{
+ d_func()->movable = b;
+}
+
+bool Q3TitleBar::isMovable() const
+{
+ return d_func()->movable;
+}
+
+void Q3TitleBar::setAutoRaise(bool b)
+{
+ d_func()->autoraise = b;
+}
+
+bool Q3TitleBar::autoRaise() const
+{
+ return d_func()->autoraise;
+}
+
+QSize Q3TitleBar::sizeHint() const
+{
+ ensurePolished();
+ QStyleOptionTitleBar opt = d_func()->getStyleOption();
+ QRect menur = style()->subControlRect(QStyle::CC_TitleBar, &opt,
+ QStyle::SC_TitleBarSysMenu, this);
+ return QSize(menur.width(), style()->pixelMetric(QStyle::PM_TitleBarHeight, &opt, this));
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_TITLEBAR
diff --git a/src/qt3support/widgets/q3titlebar_p.h b/src/qt3support/widgets/q3titlebar_p.h
new file mode 100644
index 0000000..b14b3bd
--- /dev/null
+++ b/src/qt3support/widgets/q3titlebar_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TITLEBAR_P_H
+#define Q3TITLEBAR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwidget.h"
+#include "qstyleoption.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_TITLEBAR)
+
+class QToolTip;
+class Q3TitleBarPrivate;
+class QPixmap;
+
+class Q_COMPAT_EXPORT Q3TitleBar : public QWidget
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(Q3TitleBar)
+ Q_PROPERTY(bool autoRaise READ autoRaise WRITE setAutoRaise)
+ Q_PROPERTY(bool movable READ isMovable WRITE setMovable)
+
+public:
+ Q3TitleBar (QWidget *w, QWidget *parent, Qt::WindowFlags f = 0);
+ ~Q3TitleBar();
+
+ bool isActive() const;
+ bool usesActiveColor() const;
+
+ bool isMovable() const;
+ void setMovable(bool);
+
+ bool autoRaise() const;
+ void setAutoRaise(bool);
+
+ QWidget *window() const;
+ bool isTool() const;
+
+ QSize sizeHint() const;
+ QStyleOptionTitleBar getStyleOption() const;
+
+ // These flags are fake, stored in QTitleBarPrivate. They are not set as the widget's window flags.
+ void setFakeWindowFlags(Qt::WindowFlags f);
+ Qt::WindowFlags fakeWindowFlags() const;
+
+public Q_SLOTS:
+ void setActive(bool);
+
+Q_SIGNALS:
+ void doActivate();
+ void doNormal();
+ void doClose();
+ void doMaximize();
+ void doMinimize();
+ void doShade();
+ void showOperationMenu();
+ void popupOperationMenu(const QPoint&);
+ void doubleClicked();
+
+protected:
+ bool event(QEvent *);
+ void resizeEvent(QResizeEvent *);
+ void contextMenuEvent(QContextMenuEvent *);
+ void changeEvent(QEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseDoubleClickEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void enterEvent(QEvent *e);
+ void leaveEvent(QEvent *e);
+ void paintEvent(QPaintEvent *p);
+
+ virtual void cutText();
+
+private:
+ Q_DISABLE_COPY(Q3TitleBar)
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif //Q3TITLEBAR_P_H
diff --git a/src/qt3support/widgets/q3toolbar.cpp b/src/qt3support/widgets/q3toolbar.cpp
new file mode 100644
index 0000000..3b3f508
--- /dev/null
+++ b/src/qt3support/widgets/q3toolbar.cpp
@@ -0,0 +1,840 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3toolbar.h"
+#ifndef QT_NO_TOOLBAR
+
+#include "q3mainwindow.h"
+#include "qapplication.h"
+#include "q3combobox.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "qdrawutil.h"
+#include "qevent.h"
+#include "qframe.h"
+#include "qlayout.h"
+#include "qmap.h"
+#include "qpainter.h"
+#include "q3popupmenu.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "qtimer.h"
+#include "qtoolbutton.h"
+#include "qtooltip.h"
+
+QT_BEGIN_NAMESPACE
+
+static const char * const arrow_v_xpm[] = {
+ "7 9 3 1",
+ " c None",
+ ". c #000000",
+ "+ c none",
+ ".+++++.",
+ "..+++..",
+ "+..+..+",
+ "++...++",
+ ".++.++.",
+ "..+++..",
+ "+..+..+",
+ "++...++",
+ "+++.+++"};
+
+static const char * const arrow_h_xpm[] = {
+ "9 7 3 1",
+ " c None",
+ ". c #000000",
+ "+ c none",
+ "..++..+++",
+ "+..++..++",
+ "++..++..+",
+ "+++..++..",
+ "++..++..+",
+ "+..++..++",
+ "..++..+++"};
+
+class Q3ToolBarExtensionWidget;
+
+class Q3ToolBarPrivate
+{
+public:
+ Q3ToolBarPrivate() : moving(false), checkingExtension(false) {
+ }
+
+ bool moving;
+ bool checkingExtension;
+ Q3ToolBarExtensionWidget *extension;
+ Q3PopupMenu *extensionPopup;
+
+ QMap<QAction *, QWidget *> actions;
+};
+
+
+class Q3ToolBarSeparator : public QWidget
+{
+ Q_OBJECT
+public:
+ Q3ToolBarSeparator(Qt::Orientation, Q3ToolBar *parent, const char* name=0);
+
+ QSize sizeHint() const;
+ Qt::Orientation orientation() const { return orient; }
+public slots:
+ void setOrientation(Qt::Orientation);
+protected:
+ void changeEvent(QEvent *);
+ void paintEvent(QPaintEvent *);
+
+private:
+ Qt::Orientation orient;
+};
+
+class Q3ToolBarExtensionWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Q3ToolBarExtensionWidget(QWidget *w);
+ void setOrientation(Qt::Orientation o);
+ QToolButton *button() const { return tb; }
+
+protected:
+ void resizeEvent(QResizeEvent *e) {
+ QWidget::resizeEvent(e);
+ layOut();
+ }
+
+private:
+ void layOut();
+ QToolButton *tb;
+ Qt::Orientation orient;
+
+};
+
+Q3ToolBarExtensionWidget::Q3ToolBarExtensionWidget(QWidget *w)
+ : QWidget(w, "qt_dockwidget_internal")
+{
+ tb = new QToolButton(this, "qt_toolbar_ext_button");
+ tb->setAutoRaise(true);
+ setOrientation(Qt::Horizontal);
+ setAutoFillBackground(true);
+}
+
+void Q3ToolBarExtensionWidget::setOrientation(Qt::Orientation o)
+{
+ orient = o;
+ if (orient == Qt::Horizontal)
+ tb->setIcon(QPixmap((const char **)arrow_h_xpm));
+ else
+ tb->setIcon(QPixmap((const char **)arrow_v_xpm));
+ layOut();
+}
+
+void Q3ToolBarExtensionWidget::layOut()
+{
+ tb->setGeometry(2, 2, width() - 4, height() - 4);
+}
+
+Q3ToolBarSeparator::Q3ToolBarSeparator(Qt::Orientation o , Q3ToolBar *parent,
+ const char* name)
+ : QWidget(parent, name)
+{
+ connect(parent, SIGNAL(orientationChanged(Qt::Orientation)),
+ this, SLOT(setOrientation(Qt::Orientation)));
+ setOrientation(o);
+ setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
+}
+
+
+
+void Q3ToolBarSeparator::setOrientation(Qt::Orientation o)
+{
+ orient = o;
+}
+
+void Q3ToolBarSeparator::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::StyleChange)
+ setOrientation(orient);
+ QWidget::changeEvent(ev);
+}
+
+static QStyleOption getStyleOption(const Q3ToolBarSeparator *tbs)
+{
+ QStyleOption opt(0);
+ opt.rect = tbs->rect();
+ opt.palette = tbs->palette();
+ if (tbs->orientation() == Qt::Horizontal)
+ opt.state = QStyle::State_Horizontal;
+ else
+ opt.state = QStyle::State_None;
+ return opt;
+}
+
+QSize Q3ToolBarSeparator::sizeHint() const
+{
+ QStyleOption opt = getStyleOption(this);
+ int extent = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, this);
+ if (orient == Qt::Horizontal)
+ return QSize(extent, 0);
+ else
+ return QSize(0, extent);
+}
+
+void Q3ToolBarSeparator::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOption opt = getStyleOption(this);
+ style()->drawPrimitive(QStyle::PE_Q3DockWindowSeparator, &opt, &p, this);
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "q3toolbar.moc"
+QT_END_INCLUDE_NAMESPACE
+
+
+/*!
+ \class Q3ToolBar
+ \brief The Q3ToolBar class provides a movable panel containing
+ widgets such as tool buttons.
+
+ \compat
+
+ A toolbar is a panel that contains a set of controls, usually
+ represented by small icons. It's purpose is to provide quick
+ access to frequently used commands or options. Within a
+ Q3MainWindow the user can drag toolbars within and between the
+ \link Q3DockArea dock areas\endlink. Toolbars can also be dragged
+ out of any dock area to float freely as top-level windows.
+
+ Q3ToolBar is a specialization of QDockWindow, and so provides all
+ the functionality of a QDockWindow.
+
+ To use Q3ToolBar you simply create a Q3ToolBar as a child of a
+ Q3MainWindow, create a number of QToolButton widgets (or other
+ widgets) in left to right (or top to bottom) order and call
+ addSeparator() when you want a separator. When a toolbar is
+ floated the caption used is the label given in the constructor
+ call. This can be changed with setLabel().
+
+ You may use most widgets within a toolbar, with QToolButton and
+ QComboBox being the most common. But note that the toolbar's
+ actions must be \l {Q3Action}s.
+
+ If you create a new widget on an already visible Q3ToolBar, this
+ widget will automatically become visible without needing a show()
+ call. (This differs from every other Qt widget container. We
+ recommend calling show() anyway since we hope to fix this anomaly
+ in a future release.)
+
+ Q3ToolBars, like QDockWindows, are located in \l{Q3DockArea}s or
+ float as top-level windows. Q3MainWindow provides four Q3DockAreas
+ (top, left, right and bottom). When you create a new toolbar (as
+ in the example above) as a child of a Q3MainWindow the toolbar will
+ be added to the top dock area. You can move it to another dock
+ area (or float it) by calling Q3MainWindow::moveDockWindow(). Dock
+ areas lay out their windows in lines.
+
+ If the main window is resized so that the area occupied by the
+ toolbar is too small to show all its widgets a little arrow button
+ (which looks like a right-pointing chevron, '&#187;') will appear
+ at the right or bottom of the toolbar depending on its
+ orientation. Clicking this button pops up a menu that shows the
+ 'overflowing' items. QToolButtons are represented in the menu using
+ their textLabel property, other QAbstractButton subclasses are represented
+ using their text property, and QComboBoxes are represented as submenus,
+ with the caption text being used in the submenu item.
+
+ Usually a toolbar will get precisely the space it needs. However,
+ with setHorizontalStretchable(), setVerticalStretchable() or
+ setStretchableWidget() you can tell the main window to expand the
+ toolbar to fill all available space in the specified orientation.
+
+ The toolbar arranges its buttons either horizontally or vertically
+ (see orientation() for details). Generally, Q3DockArea will set the
+ orientation correctly for you, but you can set it yourself with
+ setOrientation() and track any changes by connecting to the
+ orientationChanged() signal.
+
+ You can use the clear() method to remove all items from a toolbar.
+
+ \img qdockwindow.png Toolbar (dock window)
+ \caption A floating QToolbar (dock window)
+
+ \sa QToolButton Q3MainWindow
+*/
+
+/*!
+ Constructs an empty toolbar.
+
+ The toolbar is called \a name and is a child of \a parent and is
+ managed by \a parent. It is initially located in dock area \a dock
+ and is labeled \a label. If \a newLine is true the toolbar will be
+ placed on a new line in the dock area.
+*/
+
+Q3ToolBar::Q3ToolBar(const QString &label,
+ Q3MainWindow * parent, Qt::ToolBarDock dock,
+ bool newLine, const char * name)
+ : Q3DockWindow(InDock, parent, name, 0, true)
+{
+ mw = parent;
+ init();
+
+ if (parent)
+ parent->addToolBar(this, label, dock, newLine);
+}
+
+
+/*!
+ Constructs an empty horizontal toolbar.
+
+ The toolbar is called \a name and is a child of \a parent and is
+ managed by \a mainWindow. The \a label and \a newLine parameters
+ are passed straight to Q3MainWindow::addDockWindow(). \a name and
+ the widget flags \a f are passed on to the Q3DockWindow constructor.
+
+ Use this constructor if you want to create torn-off (undocked,
+ floating) toolbars or toolbars in the \link QStatusBar status
+ bar\endlink.
+*/
+
+Q3ToolBar::Q3ToolBar(const QString &label, Q3MainWindow * mainWindow,
+ QWidget * parent, bool newLine, const char * name,
+ Qt::WindowFlags f)
+ : Q3DockWindow(InDock, parent, name, f, true)
+{
+ mw = mainWindow;
+ init();
+
+ setParent(parent);
+
+ if (mainWindow)
+ mainWindow->addToolBar(this, label, Qt::DockUnmanaged, newLine);
+}
+
+
+/*!
+ \overload
+
+ Constructs an empty toolbar called \a name, with parent \a parent,
+ in its \a parent's top dock area, without any label and without
+ requiring a newline.
+*/
+
+Q3ToolBar::Q3ToolBar(Q3MainWindow * parent, const char * name)
+ : Q3DockWindow(InDock, parent, name, 0, true)
+{
+ mw = parent;
+ init();
+
+ if (parent)
+ parent->addToolBar(this, QString(), Qt::DockTop);
+}
+
+/*!
+ \internal
+
+ Common initialization code. Requires that \c mw and \c o are set.
+ Does not call Q3MainWindow::addDockWindow().
+*/
+void Q3ToolBar::init()
+{
+ d = new Q3ToolBarPrivate;
+ d->extension = 0;
+ d->extensionPopup = 0;
+ sw = 0;
+
+ setBackgroundRole(QPalette::Button);
+ setFocusPolicy(Qt::NoFocus);
+ setFrameStyle(QFrame::ToolBarPanel | QFrame::Raised);
+}
+
+/*!
+ Destructor.
+*/
+
+Q3ToolBar::~Q3ToolBar()
+{
+ delete d;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3ToolBar::setOrientation(Qt::Orientation o)
+{
+ Q3DockWindow::setOrientation(o);
+ if (d->extension)
+ d->extension->setOrientation(o);
+ QObjectList childList = children();
+ for (int i = 0; i < childList.size(); ++i) {
+ Q3ToolBarSeparator* w = qobject_cast<Q3ToolBarSeparator*>(childList.at(i));
+ if (w)
+ w->setOrientation(o);
+ }
+}
+
+/*!
+ Adds a separator to the right/bottom of the toolbar.
+*/
+
+void Q3ToolBar::addSeparator()
+{
+ (void) new Q3ToolBarSeparator(orientation(), this, "toolbar separator");
+}
+
+/*!
+ \internal
+*/
+
+void Q3ToolBar::styleChange(QStyle &oldStyle)
+{
+ Q3DockWindow::styleChange(oldStyle);
+}
+
+
+/*!
+ \reimp
+*/
+void Q3ToolBar::setVisible(bool visible)
+{
+ Q3DockWindow::setVisible(visible);
+ if (mw)
+ mw->triggerLayout(false);
+ if (visible)
+ checkForExtension(size());
+}
+
+/*!
+ Returns a pointer to the Q3MainWindow which manages this toolbar.
+*/
+
+Q3MainWindow * Q3ToolBar::mainWindow() const
+{
+ return mw;
+}
+
+
+/*!
+ Sets the widget \a w to be expanded if this toolbar is requested
+ to stretch.
+
+ The request to stretch might occur because Q3MainWindow
+ right-justifies the dock area the toolbar is in, or because this
+ toolbar's isVerticalStretchable() or isHorizontalStretchable() is
+ set to true.
+
+ If you call this function and the toolbar is not yet stretchable,
+ setStretchable() is called.
+
+ \sa Q3MainWindow::setRightJustification(), setVerticalStretchable(),
+ setHorizontalStretchable()
+*/
+
+void Q3ToolBar::setStretchableWidget(QWidget * w)
+{
+ sw = w;
+ boxLayout()->setStretchFactor(w, 1);
+
+ if (!isHorizontalStretchable() && !isVerticalStretchable()) {
+ if (orientation() == Qt::Horizontal)
+ setHorizontalStretchable(true);
+ else
+ setVerticalStretchable(true);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+
+bool Q3ToolBar::event(QEvent * e)
+{
+ bool r = Q3DockWindow::event(e);
+ // After the event filters have dealt with it, do our stuff.
+ if (e->type() == QEvent::ChildInserted) {
+ QObject * child = ((QChildEvent*)e)->child();
+ if (child && child->isWidgetType() && !((QWidget*)child)->isWindow()
+ && child->parent() == this
+ && QLatin1String("qt_dockwidget_internal") != child->objectName()) {
+ boxLayout()->addWidget((QWidget*)child);
+ QLayoutItem *item = boxLayout()->itemAt(boxLayout()->indexOf((QWidget*)child));
+ if (QToolButton *button = qobject_cast<QToolButton*>(child)) {
+ item->setAlignment(Qt::AlignHCenter);
+ button->setFocusPolicy(Qt::NoFocus);
+ if (mw) {
+ QObject::connect(mw, SIGNAL(pixmapSizeChanged(bool)),
+ button, SLOT(setUsesBigPixmap(bool)));
+ button->setUsesBigPixmap(mw->usesBigPixmaps());
+ QObject::connect(mw, SIGNAL(usesTextLabelChanged(bool)),
+ child, SLOT(setUsesTextLabel(bool)));
+ button->setUsesTextLabel(mw->usesTextLabel());
+ }
+ button->setAutoRaise(true);
+ }
+ if (isVisible()) {
+ // toolbar compatibility: we auto show widgets that
+ // are not explicitly hidden
+ if (((QWidget*)child)->testAttribute(Qt::WA_WState_Hidden)
+ && !((QWidget*)child)->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ ((QWidget*)child)->show();
+ checkForExtension(size());
+ }
+ }
+ if (child && child->isWidgetType() && ((QWidget*)child) == sw)
+ boxLayout()->setStretchFactor((QWidget*)child, 1);
+ } else if (e->type() == QEvent::Show) {
+ layout()->activate();
+ } else if (e->type() == QEvent::LayoutHint && place() == OutsideDock) {
+ adjustSize();
+ }
+ return r;
+}
+
+
+/*!
+ \property Q3ToolBar::label
+ \brief the toolbar's label.
+
+ If the toolbar is floated the label becomes the toolbar window's
+ caption. There is no default label text.
+*/
+
+void Q3ToolBar::setLabel(const QString & label)
+{
+ l = label;
+ setWindowTitle(l);
+}
+
+QString Q3ToolBar::label() const
+{
+ return l;
+}
+
+
+/*!
+ Deletes all the toolbar's child widgets.
+*/
+
+void Q3ToolBar::clear()
+{
+ QObjectList childList = children();
+ d->extension = 0;
+ d->extensionPopup = 0; //they will both be destroyed by the following code
+ for (int i = 0; i < childList.size(); ++i) {
+ QObject *obj = childList.at(i);
+ if (obj->isWidgetType() && QLatin1String("qt_dockwidget_internal") != obj->objectName())
+ delete obj;
+ }
+}
+
+/*!
+ \internal
+*/
+
+QSize Q3ToolBar::minimumSize() const
+{
+ if (orientation() == Qt::Horizontal)
+ return QSize(0, Q3DockWindow::minimumSize().height());
+ return QSize(Q3DockWindow::minimumSize().width(), 0);
+}
+
+/*!
+ \reimp
+*/
+
+QSize Q3ToolBar::minimumSizeHint() const
+{
+ if (orientation() == Qt::Horizontal)
+ return QSize(0, Q3DockWindow::minimumSizeHint().height());
+ return QSize(Q3DockWindow::minimumSizeHint().width(), 0);
+}
+
+void Q3ToolBar::createPopup()
+{
+ if (!d->extensionPopup) {
+ d->extensionPopup = new Q3PopupMenu(this, "qt_dockwidget_internal");
+ connect(d->extensionPopup, SIGNAL(aboutToShow()), this, SLOT(createPopup()));
+ }
+
+ if (!d->extension) {
+ d->extension = new Q3ToolBarExtensionWidget(this);
+ d->extension->setOrientation(orientation());
+ d->extension->button()->setPopup(d->extensionPopup);
+ d->extension->button()->setPopupMode(QToolButton::InstantPopup);
+ }
+
+ d->extensionPopup->clear();
+
+ // delete submenus
+ QObjectList popups = d->extensionPopup->queryList("Q3PopupMenu", 0, false, true);
+ while (!popups.isEmpty())
+ delete popups.takeFirst();
+
+ QObjectList childlist = queryList("QWidget", 0, false, true);
+ bool hide = false;
+ bool doHide = false;
+ int id;
+ for (int i = 0; i < childlist.size(); ++i) {
+ QObject *obj = childlist.at(i);
+ if (!obj->isWidgetType() || obj == d->extension->button() || obj == d->extensionPopup
+ || QLatin1String("qt_dockwidget_internal") == obj->objectName()) {
+ continue;
+ }
+ int j = 2;
+ QWidget *w = (QWidget*)obj;
+ if (qobject_cast<Q3ComboBox*>(w))
+ j = 1;
+ hide = false;
+
+ const int padding = 4; // extra pixels added by the layout hierarchy
+ QPoint p(mapTo(this, w->geometry().bottomRight()));
+ if (orientation() == Qt::Horizontal) {
+ if ((p.x() > (doHide ? width() - d->extension->width() / j - padding : width() - padding))
+ || (p.x() > parentWidget()->width() - d->extension->width()))
+ hide = true;
+ } else {
+ if ((p.y() > (doHide ? height()- d->extension->height() / j - padding : height() - padding))
+ || (p.y() > parentWidget()->height() - d->extension->height()))
+ hide = true;
+ }
+ if (hide && w->isVisible()) {
+ doHide = true;
+ if (qobject_cast<QToolButton*>(w)) {
+ QToolButton *b = (QToolButton*)w;
+ QString s = b->textLabel();
+ if (s.isEmpty())
+ s = b->text();
+ if (b->popup() && b->popupDelay() == 0)
+ id = d->extensionPopup->insertItem(b->iconSet(), s, b->popup());
+ else
+ id = d->extensionPopup->insertItem(b->iconSet(), s, b, SLOT(click())) ;
+ if (b->isToggleButton())
+ d->extensionPopup->setItemChecked(id, b->isOn());
+ if (!b->isEnabled())
+ d->extensionPopup->setItemEnabled(id, false);
+ } else if (qobject_cast<QAbstractButton*>(w)) {
+ QAbstractButton *b = (QAbstractButton*)w;
+ QString s = b->text();
+ if (s.isEmpty())
+ s = QLatin1String("");
+ if (b->pixmap())
+ id = d->extensionPopup->insertItem(*b->pixmap(), s, b, SLOT(click()));
+ else
+ id = d->extensionPopup->insertItem(s, b, SLOT(click()));
+ if (b->isToggleButton())
+ d->extensionPopup->setItemChecked(id, b->isOn());
+ if (!b->isEnabled())
+ d->extensionPopup->setItemEnabled(id, false);
+#ifndef QT_NO_COMBOBOX
+ } else if (qobject_cast<Q3ComboBox*>(w)) {
+ Q3ComboBox *c = (Q3ComboBox*)w;
+ if (c->count() != 0) {
+ QString s = c->windowTitle();
+ if (s.isEmpty())
+ s = c->currentText();
+ int maxItems = 0;
+ Q3PopupMenu *cp = new Q3PopupMenu(d->extensionPopup);
+ cp->setEnabled(c->isEnabled());
+ d->extensionPopup->insertItem(s, cp);
+ connect(cp, SIGNAL(activated(int)), c, SLOT(internalActivate(int)));
+ for (int i = 0; i < c->count(); ++i) {
+ QString tmp = c->text(i);
+ cp->insertItem(tmp, i);
+ if (c->currentText() == tmp)
+ cp->setItemChecked(i, true);
+ if (!maxItems) {
+ if (cp->actions().count() == 10) {
+ int h = cp->sizeHint().height();
+ maxItems = QApplication::desktop()->height() * 10 / h;
+ }
+ } else if (cp->actions().count() >= maxItems - 1) {
+ Q3PopupMenu* sp = new Q3PopupMenu(d->extensionPopup);
+ cp->insertItem(tr("More..."), sp);
+ cp = sp;
+ connect(cp, SIGNAL(activated(int)), c, SLOT(internalActivate(int)));
+ }
+ }
+ }
+#endif //QT_NO_COMBOBOX
+ }
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3ToolBar::resizeEvent(QResizeEvent *e)
+{
+ Q3DockWindow::resizeEvent(e);
+ checkForExtension(e->size());
+}
+
+/*!
+ \internal
+
+ This function is called when an action is triggered. The relevant
+ information is passed in the event \a e.
+*/
+void Q3ToolBar::actionEvent(QActionEvent *e)
+{
+ if (e->type() == QEvent::ActionAdded) {
+ QAction *a = e->action();
+ QWidget *w;
+ if (a->isSeparator()) {
+ w = new Q3ToolBarSeparator(orientation(), this, "toolbar separator");
+ } else {
+ QToolButton* btn = new QToolButton(this);
+ btn->setDefaultAction(a);
+ w = btn;
+ }
+ d->actions.insert(a, w);
+ } else if (e->type() == QEvent::ActionRemoved) {
+ QAction *a = e->action();
+ delete d->actions.take(a);
+ }
+}
+
+
+void Q3ToolBar::checkForExtension(const QSize &sz)
+{
+ if (!isVisible())
+ return;
+
+ if (d->checkingExtension)
+ return;
+ d->checkingExtension = true;
+
+ bool tooSmall;
+ if (orientation() == Qt::Horizontal)
+ tooSmall = sz.width() < sizeHint().width();
+ else
+ tooSmall = sz.height() < sizeHint().height();
+
+ if (tooSmall) {
+ createPopup();
+ if (d->extensionPopup->actions().count()) {
+ // parentWidget()->width() used since the Q3ToolBar width
+ // will never be less than minimumSize()
+ if (orientation() == Qt::Horizontal)
+ d->extension->setGeometry((parentWidget() ? parentWidget()->width() : width()) - 20,
+ 1, 20, height() - 2);
+ else
+ d->extension->setGeometry(1, (parentWidget() ? parentWidget()->height() : height()) - 20,
+ width() - 2, 20);
+ d->extension->show();
+ d->extension->raise();
+ } else {
+ delete d->extension;
+ d->extension = 0;
+ delete d->extensionPopup;
+ d->extensionPopup = 0;
+ }
+ } else {
+ delete d->extension;
+ d->extension = 0;
+ delete d->extensionPopup;
+ d->extensionPopup = 0;
+ }
+ d->checkingExtension = false;
+}
+
+
+/*!
+ \internal
+*/
+
+void Q3ToolBar::setMinimumSize(int, int)
+{
+}
+
+/* from chaunsee:
+
+1. Tool Bars should contain only high-frequency functions. Avoid putting
+things like About and Exit on a tool bar unless they are frequent functions.
+
+2. All tool bar buttons must have some keyboard access method (it can be a
+menu or shortcut key or a function in a dialog box that can be accessed
+through the keyboard).
+
+3. Make tool bar functions as efficient as possible (the common example is to
+Print in Microsoft applications, it doesn't bring up the Print dialog box, it
+prints immediately to the default printer).
+
+4. Avoid turning tool bars into graphical menu bars. To me, a tool bar should
+be efficient. Once you make almost all the items in a tool bar into graphical
+pull-down menus, you start to lose efficiency.
+
+5. Make sure that adjacent icons are distinctive. There are some tool bars
+where you see a group of 4-5 icons that represent related functions, but they
+are so similar that you can't differentiate among them. These tool bars are
+often a poor attempt at a "common visual language".
+
+6. Use any de facto standard icons of your platform (for windows use the
+cut, copy and paste icons provided in dev kits rather than designing your
+own).
+
+7. Avoid putting a highly destructive tool bar button (delete database) by a
+safe, high-frequency button (Find) -- this will yield 1-0ff errors).
+
+8. Tooltips in many Microsoft products simply reiterate the menu text even
+when that is not explanatory. Consider making your tooltips slightly more
+verbose and explanatory than the corresponding menu item.
+
+9. Keep the tool bar as stable as possible when you click on different
+objects. Consider disabling tool bar buttons if they are used in most, but not
+all contexts.
+
+10. If you have multiple tool bars (like the Microsoft MMC snap-ins have),
+put the most stable tool bar to at the left with less stable ones to the
+right. This arrangement (stable to less stable) makes the tool bar somewhat
+more predictable.
+
+11. Keep a single tool bar to fewer than 20 items divided into 4-7 groups of
+items.
+*/
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/widgets/q3toolbar.h b/src/qt3support/widgets/q3toolbar.h
new file mode 100644
index 0000000..9e5c514
--- /dev/null
+++ b/src/qt3support/widgets/q3toolbar.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TOOLBAR_H
+#define Q3TOOLBAR_H
+
+#include <Qt3Support/q3dockwindow.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_TOOLBAR
+
+class Q3MainWindow;
+class Q3ToolBarPrivate;
+
+class Q_COMPAT_EXPORT Q3ToolBar: public Q3DockWindow
+{
+ Q_OBJECT
+ Q_PROPERTY(QString label READ label WRITE setLabel)
+
+public:
+ Q3ToolBar(const QString &label,
+ Q3MainWindow *, Qt::ToolBarDock = Qt::DockTop,
+ bool newLine = false, const char* name=0);
+ Q3ToolBar(const QString &label, Q3MainWindow *, QWidget *,
+ bool newLine = false, const char* name=0, Qt::WindowFlags f = 0);
+ Q3ToolBar(Q3MainWindow* parent=0, const char* name=0);
+ ~Q3ToolBar();
+
+ void addSeparator();
+
+ void setVisible(bool visible);
+
+ Q3MainWindow * mainWindow() const;
+
+ virtual void setStretchableWidget(QWidget *);
+
+ bool event(QEvent * e);
+
+ virtual void setLabel(const QString &);
+ QString label() const;
+
+ virtual void clear();
+
+ QSize minimumSize() const;
+ QSize minimumSizeHint() const;
+
+ void setOrientation(Qt::Orientation o);
+ void setMinimumSize(int minw, int minh);
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+ void styleChange(QStyle &);
+ void actionEvent(QActionEvent *);
+
+private Q_SLOTS:
+ void createPopup();
+
+private:
+ void init();
+ void checkForExtension(const QSize &sz);
+ Q3ToolBarPrivate * d;
+ Q3MainWindow * mw;
+ QWidget * sw;
+ QString l;
+
+ friend class Q3MainWindow;
+ friend class Q3DockAreaLayout;
+
+private:
+ Q_DISABLE_COPY(Q3ToolBar)
+};
+
+#endif // QT_NO_TOOLBAR
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TOOLBAR_H
diff --git a/src/qt3support/widgets/q3vbox.cpp b/src/qt3support/widgets/q3vbox.cpp
new file mode 100644
index 0000000..82112cd
--- /dev/null
+++ b/src/qt3support/widgets/q3vbox.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "q3vbox.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3VBox
+ \brief The Q3VBox widget provides vertical geometry management of
+ its child widgets.
+
+ \compat
+
+ All its child widgets will be placed vertically and sized
+ according to their sizeHint()s.
+
+ \img qvbox-m.png Q3VBox
+
+ \sa Q3HBox
+*/
+
+
+/*!
+ Constructs a vbox widget called \a name with parent \a parent and
+ widget flags \a f.
+ */
+Q3VBox::Q3VBox( QWidget *parent, const char *name, Qt::WindowFlags f )
+ :Q3HBox( false, parent, name, f )
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3vbox.h b/src/qt3support/widgets/q3vbox.h
new file mode 100644
index 0000000..34fedb7
--- /dev/null
+++ b/src/qt3support/widgets/q3vbox.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3VBOX_H
+#define Q3VBOX_H
+
+#include <Qt3Support/q3hbox.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3VBox : public Q3HBox
+{
+ Q_OBJECT
+public:
+ Q3VBox(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0);
+
+private:
+ Q_DISABLE_COPY(Q3VBox)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3VBOX_H
diff --git a/src/qt3support/widgets/q3vgroupbox.cpp b/src/qt3support/widgets/q3vgroupbox.cpp
new file mode 100644
index 0000000..c39a6d7
--- /dev/null
+++ b/src/qt3support/widgets/q3vgroupbox.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3vgroupbox.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3VGroupBox
+
+ \brief The Q3VGroupBox widget organizes widgets in a group with one
+ vertical column.
+
+ \compat
+
+ Q3VGroupBox is a convenience class that offers a thin layer on top
+ of Q3GroupBox. Think of it as a Q3VBox that offers a frame with a
+ title.
+
+ \sa Q3HGroupBox
+*/
+
+/*!
+ Constructs a horizontal group box with no title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+Q3VGroupBox::Q3VGroupBox( QWidget *parent, const char *name )
+ : Q3GroupBox( 1, Qt::Horizontal /* sic! */, parent, name )
+{
+}
+
+/*!
+ Constructs a horizontal group box with the title \a title.
+
+ The \a parent and \a name arguments are passed to the QWidget
+ constructor.
+*/
+
+Q3VGroupBox::Q3VGroupBox( const QString &title, QWidget *parent,
+ const char *name )
+ : Q3GroupBox( 1, Qt::Horizontal /* sic! */, title, parent, name )
+{
+}
+
+/*!
+ Destroys the horizontal group box, deleting its child widgets.
+*/
+Q3VGroupBox::~Q3VGroupBox()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3vgroupbox.h b/src/qt3support/widgets/q3vgroupbox.h
new file mode 100644
index 0000000..f4fb5ed
--- /dev/null
+++ b/src/qt3support/widgets/q3vgroupbox.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3VGROUPBOX_H
+#define Q3VGROUPBOX_H
+
+#include <Qt3Support/q3groupbox.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q_COMPAT_EXPORT Q3VGroupBox : public Q3GroupBox
+{
+ Q_OBJECT
+public:
+ Q3VGroupBox( QWidget* parent=0, const char* name=0 );
+ Q3VGroupBox( const QString &title, QWidget* parent=0, const char* name=0 );
+ ~Q3VGroupBox();
+
+private:
+ Q_DISABLE_COPY(Q3VGroupBox)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3VGROUPBOX_H
diff --git a/src/qt3support/widgets/q3whatsthis.cpp b/src/qt3support/widgets/q3whatsthis.cpp
new file mode 100644
index 0000000..30d3801
--- /dev/null
+++ b/src/qt3support/widgets/q3whatsthis.cpp
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3whatsthis.h"
+#ifndef QT_NO_WHATSTHIS
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qevent.h"
+
+QT_BEGIN_NAMESPACE
+
+/*! \class Q3WhatsThis
+ \compat
+*/
+
+/*!
+ Constructs a new "What's This?" object for \a widget.
+*/
+Q3WhatsThis::Q3WhatsThis(QWidget *widget)
+ : QObject(widget)
+{
+ if (widget)
+ widget->installEventFilter(this);
+}
+
+/*!
+ Destroys the "What's This?" object.
+*/
+Q3WhatsThis::~Q3WhatsThis()
+{
+}
+
+/*!
+ \internal
+
+ Handles "What's This?" events.
+*/
+bool Q3WhatsThis::eventFilter(QObject *o, QEvent *e)
+{
+ if (o != parent() || !o->isWidgetType())
+ return false;
+
+ if (e->type() == QEvent::WhatsThis) {
+ QString s = text(static_cast<QHelpEvent*>(e)->pos());
+ if (!s.isEmpty())
+ QWhatsThis::showText(static_cast<QHelpEvent*>(e)->globalPos(), s, static_cast<QWidget*>(o));
+ } else if (e->type() == QEvent::QueryWhatsThis) {
+ QString s = text(static_cast<QHelpEvent*>(e)->pos());
+ if (s.isEmpty())
+ return false;
+ } else if (e->type() == QEvent::WhatsThisClicked) {
+ QString href = static_cast<QWhatsThisClickedEvent*>(e)->href();
+ if (clicked(href))
+ QWhatsThis::hideText();
+ } else {
+ return false;
+ }
+ return true;
+}
+
+/*!
+ This virtual function returns the text for position \a pos in the
+ widget that this "What's This?" object documents. If there is no
+ "What's This?" text for the position, an empty string is returned.
+
+ The default implementation returns an empty string.
+*/
+QString Q3WhatsThis::text(const QPoint & /* pos */)
+{
+ if (parent() && parent()->isWidgetType())
+ return static_cast<QWidget*>(parent())->whatsThis();
+ return QString();
+}
+
+/*!
+ This virtual function is called when the user clicks inside the
+ "What's this?" window. \a href is the link the user clicked on, or
+ an empty string if there was no link.
+
+ If the function returns true (the default), the "What's this?"
+ window is closed, otherwise it remains visible.
+
+ The default implementation ignores \a href and returns true.
+*/
+bool Q3WhatsThis::clicked(const QString & /* href */)
+{
+ return true;
+}
+
+/*!
+ \fn void Q3WhatsThis::enterWhatsThisMode()
+
+ Enters "What's This?" mode and returns immediately.
+
+ Qt will install a special cursor and take over mouse input until
+ the user clicks somewhere. It then shows any help available and
+ ends "What's This?" mode. Finally, Qt removes the special cursor
+ and help window and then restores ordinary event processing, at
+ which point the left mouse button is no longer pressed.
+
+ The user can also use the Esc key to leave "What's This?" mode.
+
+ \sa inWhatsThisMode(), leaveWhatsThisMode()
+*/
+
+/*!
+ \fn bool Q3WhatsThis::inWhatsThisMode()
+
+ Returns true if the application is in "What's This?" mode;
+ otherwise returns false.
+
+ \sa enterWhatsThisMode(), leaveWhatsThisMode()
+*/
+
+/*!
+ \fn void Q3WhatsThis::add(QWidget *widget, const QString &text)
+
+ Adds \a text as "What's This?" help for \a widget. If the text is
+ rich text formatted (i.e. it contains markup) it will be rendered
+ with the default stylesheet QStyleSheet::defaultSheet().
+
+ The text is destroyed if the widget is later destroyed, so it need
+ not be explicitly removed.
+
+ \sa remove()
+*/
+
+/*!
+ \fn void Q3WhatsThis::remove(QWidget *widget)
+
+ Removes the "What's This?" help associated with the \a widget.
+ This happens automatically if the widget is destroyed.
+
+ \sa add()
+*/
+
+/*!
+ \fn void Q3WhatsThis::leaveWhatsThisMode(const QString& text = QString(), const QPoint& pos = QCursor::pos(), QWidget* widget = 0)
+
+ This function is used internally by widgets that support
+ QWidget::customWhatsThis(); applications do not usually call it.
+ An example of such a widget is Q3PopupMenu: menus still work
+ normally in "What's This?" mode but also provide help texts for
+ individual menu items.
+
+ If \a text is not empty, a "What's This?" help window is
+ displayed at the global screen position \a pos. If widget \a widget is
+ not 0 and has its own dedicated QWhatsThis object, this object
+ will receive clicked() messages when the user clicks on hyperlinks
+ inside the help text.
+
+ \sa inWhatsThisMode(), enterWhatsThisMode(), clicked()
+*/
+
+/*!
+ \fn void Q3WhatsThis::display(const QString &text, const QPoint &pos, QWidget *widget)
+
+ Display \a text in a help window at the global screen position \a
+ pos.
+
+ If widget \a widget is not 0 and has its own dedicated QWhatsThis
+ object, this object will receive clicked() messages when the user
+ clicks on hyperlinks inside the help text.
+
+ \sa clicked()
+*/
+
+/*!
+ Creates a QToolButton preconfigured to enter "What's This?" mode
+ when clicked. You will often use this with a tool bar as \a
+ parent:
+
+ \snippet doc/src/snippets/code/src_qt3support_widgets_q3whatsthis.cpp 0
+*/
+QToolButton *Q3WhatsThis::whatsThisButton(QWidget * parent)
+{
+ return QWhatsThis::whatsThisButton(parent);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_WHATSTHIS
diff --git a/src/qt3support/widgets/q3whatsthis.h b/src/qt3support/widgets/q3whatsthis.h
new file mode 100644
index 0000000..cf841b6
--- /dev/null
+++ b/src/qt3support/widgets/q3whatsthis.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3WHATSTHIS_H
+#define Q3WHATSTHIS_H
+
+#include <QtGui/qcursor.h>
+#include <QtGui/qwhatsthis.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_WHATSTHIS
+
+class QToolButton;
+
+class Q_COMPAT_EXPORT Q3WhatsThis: public QObject
+{
+ Q_OBJECT
+public:
+ Q3WhatsThis(QWidget *);
+ ~Q3WhatsThis();
+ bool eventFilter(QObject *, QEvent *);
+
+ static inline void enterWhatsThisMode() { QWhatsThis::enterWhatsThisMode(); }
+ static inline bool inWhatsThisMode() { return QWhatsThis::inWhatsThisMode(); }
+
+ static inline void add(QWidget *w, const QString &s) { w->setWhatsThis(s); }
+ static inline void remove(QWidget *w) { w->setWhatsThis(QString()); }
+ static QToolButton * whatsThisButton(QWidget * parent);
+ static inline void leaveWhatsThisMode(const QString& text = QString(), const QPoint& pos = QCursor::pos(), QWidget* w = 0)
+ { QWhatsThis::showText(pos, text, w); }
+ static inline void display(const QString& text, const QPoint& pos = QCursor::pos(), QWidget* w = 0)
+ { QWhatsThis::showText(pos, text, w); }
+
+ virtual QString text(const QPoint &);
+ virtual bool clicked(const QString& href);
+
+};
+
+#endif // QT_NO_WHATSTHIS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3WHATSTHIS_H
diff --git a/src/qt3support/widgets/q3widgetstack.cpp b/src/qt3support/widgets/q3widgetstack.cpp
new file mode 100644
index 0000000..320c089
--- /dev/null
+++ b/src/qt3support/widgets/q3widgetstack.cpp
@@ -0,0 +1,571 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3widgetstack.h"
+#include "qlayout.h"
+#include "private/qlayoutengine_p.h"
+#include "qapplication.h"
+#include "qpainter.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt;
+
+class Q3WidgetStackPrivate {
+public:
+ class Invisible: public QWidget
+ {
+ public:
+ Invisible(Q3WidgetStack * parent): QWidget(parent, "qt_invisible_widgetstack")
+ {
+ setBackgroundMode(NoBackground);
+ }
+ const char * className() const
+ {
+ return "Q3WidgetStackPrivate::Invisible";
+ }
+ protected:
+ void paintEvent(QPaintEvent *)
+ {
+ QPainter(this).eraseRect(rect());
+ }
+ };
+
+ int nextNegativeID;
+ int nextPositiveID;
+};
+
+
+
+/*!
+ \class Q3WidgetStack
+ \brief The Q3WidgetStack class provides a stack of widgets of which
+ only the top widget is user-visible.
+
+ \compat
+
+ The application programmer can move any widget to the top of the
+ stack at any time using raiseWidget(), and add or remove widgets
+ using addWidget() and removeWidget(). It is not sufficient to pass
+ the widget stack as parent to a widget which should be inserted into
+ the widgetstack.
+
+ visibleWidget() is the \e get equivalent of raiseWidget(); it
+ returns a pointer to the widget that is currently at the top of
+ the stack.
+
+ Q3WidgetStack also provides the ability to manipulate widgets
+ through application-specified integer IDs. You can also translate
+ from widget pointers to IDs using id() and from IDs to widget
+ pointers using widget(). These numeric IDs are unique (per
+ Q3WidgetStack, not globally), but Q3WidgetStack does not attach any
+ additional meaning to them.
+
+ The default widget stack is frameless, but you can use the usual
+ Q3Frame functions (such as setFrameStyle()) to add a frame.
+
+ Q3WidgetStack provides a signal, aboutToShow(), which is emitted
+ just before a managed widget is shown.
+
+ \sa Q3TabDialog QTabWidget QTabBar Q3Frame
+*/
+
+
+/*!
+ Constructs an empty widget stack.
+
+ The \a parent, \a name and \a f arguments are passed to the Q3Frame
+ constructor.
+*/
+Q3WidgetStack::Q3WidgetStack(QWidget * parent, const char *name, Qt::WindowFlags f)
+ : Q3Frame(parent, name, f) //## merge constructors in 4.0
+{
+ init();
+}
+
+void Q3WidgetStack::init()
+{
+ d = new Q3WidgetStackPrivate();
+ d->nextNegativeID = -2;
+ d->nextPositiveID = 0;
+ dict = new Q3IntDict<QWidget>;
+ focusWidgets = 0;
+ topWidget = 0;
+ invisible = 0;
+ invisible = new Q3WidgetStackPrivate::Invisible(this);
+ invisible->hide();
+}
+
+
+/*!
+ Destroys the object and frees any allocated resources.
+*/
+
+Q3WidgetStack::~Q3WidgetStack()
+{
+ delete focusWidgets;
+ delete d;
+ delete dict;
+}
+
+
+/*!
+ Adds widget \a w to this stack of widgets, with ID \a id.
+
+ If you pass an id \>= 0 this ID is used. If you pass an \a id of
+ -1 (the default), the widgets will be numbered automatically. If
+ you pass -2 a unique negative integer will be generated. No widget
+ has an ID of -1. Returns the ID or -1 on failure (e.g. \a w is 0).
+
+ If you pass an id that is already used, then a unique negative
+ integer will be generated to prevent two widgets having the same
+ id.
+
+ If \a w already exists in the stack the widget will be removed first.
+
+ If \a w is not a child of this Q3WidgetStack moves it using
+ reparent().
+*/
+
+int Q3WidgetStack::addWidget(QWidget * w, int id)
+{
+ if (!w || w == invisible || invisible == 0)
+ return -1;
+
+ // prevent duplicates
+ removeWidget(w);
+
+ if (id >= 0 && dict->find(id))
+ id = -2;
+ if (id < -1)
+ id = d->nextNegativeID--;
+ else if (id == -1)
+ id = d->nextPositiveID++;
+ else
+ d->nextPositiveID = qMax(d->nextPositiveID, id + 1);
+ // use id >= 0 as-is
+
+ dict->insert(id, w);
+
+ // preserve existing focus
+ QWidget * f = w->focusWidget();
+ while(f && f != w)
+ f = f->parentWidget();
+ if (f) {
+ if (!focusWidgets)
+ focusWidgets = new Q3PtrDict<QWidget>(17);
+ focusWidgets->replace(w, w->focusWidget());
+ }
+
+ w->hide();
+ if (w->parent() != this)
+ w->reparent(this, contentsRect().topLeft(), false);
+ w->setGeometry(contentsRect());
+ updateGeometry();
+ return id;
+}
+
+
+/*!
+ Removes widget \a w from this stack of widgets. Does not delete \a
+ w. If \a w is the currently visible widget, no other widget is
+ substituted.
+
+ \sa visibleWidget() raiseWidget()
+*/
+
+void Q3WidgetStack::removeWidget(QWidget * w)
+{
+ int i;
+ if (!w || (i = id(w)) == -1)
+ return ;
+
+ dict->take(i);
+ if (w == topWidget)
+ topWidget = 0;
+ if (dict->isEmpty())
+ invisible->hide(); // let background shine through again
+ updateGeometry();
+}
+
+
+/*!
+ Raises the widget with ID \a id to the top of the widget stack.
+
+ \sa visibleWidget()
+*/
+
+void Q3WidgetStack::raiseWidget(int id)
+{
+ if (id == -1)
+ return;
+ QWidget * w = dict->find(id);
+ if (w)
+ raiseWidget(w);
+}
+
+static bool isChildOf(QWidget* child, QWidget *parent)
+{
+ if (!child)
+ return false;
+ QObjectList list = parent->children();
+ for (int i = 0; i < list.size(); ++i) {
+ QObject *obj = list.at(i);
+ if (!obj->isWidgetType())
+ continue;
+ QWidget *widget = static_cast<QWidget *>(obj);
+ if (!widget->isWindow())
+ continue;
+ if (widget == child || isChildOf(child, widget))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ Raises widget \a w to the top of the widget stack.
+*/
+
+void Q3WidgetStack::raiseWidget(QWidget *w)
+{
+ if (!w || w == invisible || w->parent() != this || w == topWidget)
+ return;
+
+ if (id(w) == -1)
+ addWidget(w);
+ if (!isVisible()) {
+ topWidget = w;
+ return;
+ }
+
+ if (w->maximumSize().width() < invisible->width()
+ || w->maximumSize().height() < invisible->height())
+ invisible->setBackgroundMode(backgroundMode());
+ else if (invisible->backgroundMode() != NoBackground)
+ invisible->setBackgroundMode(NoBackground);
+
+ if (invisible->isHidden()) {
+ invisible->setGeometry(contentsRect());
+ invisible->lower();
+ invisible->show();
+ QApplication::sendPostedEvents(invisible, QEvent::ShowWindowRequest);
+ }
+
+ // try to move focus onto the incoming widget if focus
+ // was somewhere on the outgoing widget.
+ if (topWidget) {
+ QWidget * fw = window()->focusWidget();
+ if (topWidget->isAncestorOf(fw)) { // focus was on old page
+ // look for the best focus widget we can find
+ QWidget *p = w->focusWidget();
+ if (!p) {
+ // second best == first child widget in the focus chain
+ QWidget *i = fw;
+ while ((i = i->nextInFocusChain()) != fw) {
+ if (((i->focusPolicy() & Qt::TabFocus) == Qt::TabFocus)
+ && !i->focusProxy() && i->isVisibleTo(w) && i->isEnabled()
+ && w->isAncestorOf(i)) {
+ p = i;
+ break;
+ }
+ }
+ }
+ if (p)
+ p->setFocus();
+ } else {
+ // the focus wasn't on the old page, so we have to ensure focus doesn't go to
+ // the widget in the page that last had focus when we show the page again.
+ QWidget *oldfw = topWidget->focusWidget();
+ if (oldfw)
+ oldfw->clearFocus();
+ }
+ }
+
+ if (isVisible()) {
+ emit aboutToShow(w);
+ int i = id(w);
+ if (i != -1)
+ emit aboutToShow(i);
+ }
+
+ topWidget = w;
+
+ QObjectList c = children();
+ for (int i = 0; i < c.size(); ++i) {
+ QObject * o = c.at(i);
+ if (o->isWidgetType() && o != w && o != invisible)
+ static_cast<QWidget *>(o)->hide();
+ }
+
+ w->setGeometry(invisible->geometry());
+ w->show();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3WidgetStack::frameChanged()
+{
+ Q3Frame::frameChanged();
+ setChildGeometries();
+}
+
+
+/*!
+ \internal
+*/
+
+void Q3WidgetStack::setFrameRect(const QRect & r)
+{
+ // ### this function used to be virtual in QFrame in Qt 3; it is no longer virtual in Qt 4
+ Q3Frame::setFrameRect(r);
+ setChildGeometries();
+}
+
+
+/*!
+ Fixes up the children's geometries.
+*/
+
+void Q3WidgetStack::setChildGeometries()
+{
+ invisible->setGeometry(contentsRect());
+ if (topWidget)
+ topWidget->setGeometry(invisible->geometry());
+}
+
+
+/*!
+ \reimp
+*/
+void Q3WidgetStack::setVisible(bool visible)
+{
+ if (visible) {
+ // Reimplemented in order to set the children's geometries
+ // appropriately and to pick the first widget as d->topWidget if no
+ // topwidget was defined
+ QObjectList c = children();
+ if (!isVisible() && !c.isEmpty()) {
+ for (int i = 0; i < c.size(); ++i) {
+ QObject * o = c.at(i);
+ if (o->isWidgetType()) {
+ if (!topWidget && o != invisible)
+ topWidget = static_cast<QWidget*>(o);
+ if (o == topWidget)
+ static_cast<QWidget *>(o)->show();
+ else
+ static_cast<QWidget *>(o)->hide();
+ }
+ }
+ setChildGeometries();
+ }
+ }
+ Q3Frame::setVisible(visible);
+}
+
+
+/*!
+ Returns the widget with ID \a id. Returns 0 if this widget stack
+ does not manage a widget with ID \a id.
+
+ \sa id() addWidget()
+*/
+
+QWidget * Q3WidgetStack::widget(int id) const
+{
+ return id != -1 ? dict->find(id) : 0;
+}
+
+
+/*!
+ Returns the ID of the \a widget. Returns -1 if \a widget is 0 or
+ is not being managed by this widget stack.
+
+ \sa widget() addWidget()
+*/
+
+int Q3WidgetStack::id(QWidget * widget) const
+{
+ if (!widget)
+ return -1;
+
+ Q3IntDictIterator<QWidget> it(*dict);
+ while (it.current() && it.current() != widget)
+ ++it;
+ return it.current() == widget ? it.currentKey() : -1;
+}
+
+
+/*!
+ Returns the currently visible widget (the one at the top of the
+ stack), or 0 if nothing is currently being shown.
+
+ \sa aboutToShow() id() raiseWidget()
+*/
+
+QWidget * Q3WidgetStack::visibleWidget() const
+{
+ return topWidget;
+}
+
+
+/*!
+ \fn void Q3WidgetStack::aboutToShow(int id)
+
+ This signal is emitted just before a managed widget is shown if
+ that managed widget has an ID != -1. The \a id parameter is the numeric
+ ID of the widget.
+
+ If you call visibleWidget() in a slot connected to aboutToShow(),
+ the widget it returns is the one that is currently visible, not
+ the one that is about to be shown.
+*/
+
+
+/*!
+ \fn void Q3WidgetStack::aboutToShow(QWidget *widget)
+
+ \overload
+
+ This signal is emitted just before a managed widget is shown. The
+ argument is a pointer to the \a widget.
+
+ If you call visibleWidget() in a slot connected to aboutToShow(),
+ the widget returned is the one that is currently visible, not the
+ one that is about to be shown.
+*/
+
+
+/*!
+ \reimp
+*/
+
+void Q3WidgetStack::resizeEvent(QResizeEvent * e)
+{
+ Q3Frame::resizeEvent(e);
+ setChildGeometries();
+}
+
+
+/*!
+ \reimp
+*/
+
+QSize Q3WidgetStack::sizeHint() const
+{
+ constPolish();
+
+ QSize size(0, 0);
+
+ Q3IntDictIterator<QWidget> it(*dict);
+ QWidget *w;
+
+ while ((w = it.current()) != 0) {
+ ++it;
+ QSize sh = w->sizeHint();
+ if (w->sizePolicy().horData() == QSizePolicy::Ignored)
+ sh.rwidth() = 0;
+ if (w->sizePolicy().verData() == QSizePolicy::Ignored)
+ sh.rheight() = 0;
+#ifndef QT_NO_LAYOUT
+ size = size.expandedTo(sh).expandedTo(qSmartMinSize(w));
+#endif
+ }
+ if (size.isNull())
+ size = QSize(128, 64);
+ size += QSize(2*frameWidth(), 2*frameWidth());
+ return size;
+}
+
+
+/*!
+ \reimp
+*/
+QSize Q3WidgetStack::minimumSizeHint() const
+{
+ constPolish();
+
+ QSize size(0, 0);
+
+ Q3IntDictIterator<QWidget> it(*dict);
+ QWidget *w;
+
+ while ((w = it.current()) != 0) {
+ ++it;
+ QSize sh = w->minimumSizeHint();
+ if (w->sizePolicy().horData() == QSizePolicy::Ignored)
+ sh.rwidth() = 0;
+ if (w->sizePolicy().verData() == QSizePolicy::Ignored)
+ sh.rheight() = 0;
+#ifndef QT_NO_LAYOUT
+ size = size.expandedTo(sh).expandedTo(w->minimumSize());
+#endif
+ }
+ if (size.isNull())
+ size = QSize(64, 32);
+ size += QSize(2*frameWidth(), 2*frameWidth());
+ return size;
+}
+
+/*!
+ \reimp
+*/
+void Q3WidgetStack::childEvent(QChildEvent *e)
+{
+ if (e->child()->isWidgetType() && e->removed())
+ removeWidget((QWidget *) e->child());
+}
+
+
+/*!
+ \reimp
+*/
+bool Q3WidgetStack::event(QEvent* e)
+{
+ if (e->type() == QEvent::LayoutRequest || e->type() == QEvent::LayoutHint )
+ updateGeometry(); // propgate layout hints to parent
+ return Q3Frame::event(e);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/widgets/q3widgetstack.h b/src/qt3support/widgets/q3widgetstack.h
new file mode 100644
index 0000000..3d10fd4
--- /dev/null
+++ b/src/qt3support/widgets/q3widgetstack.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3WIDGETSTACK_H
+#define Q3WIDGETSTACK_H
+
+#include <Qt3Support/q3frame.h>
+#include <Qt3Support/q3intdict.h>
+#include <Qt3Support/q3ptrdict.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3WidgetStackPrivate;
+
+
+class Q_COMPAT_EXPORT Q3WidgetStack: public Q3Frame
+{
+ Q_OBJECT
+public:
+ Q3WidgetStack(QWidget* parent, const char* name=0, Qt::WindowFlags f=0);
+
+ ~Q3WidgetStack();
+
+ int addWidget(QWidget *, int = -1);
+ void removeWidget(QWidget *);
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+ void setVisible(bool visible);
+
+ QWidget * widget(int) const;
+ int id(QWidget *) const;
+
+ QWidget * visibleWidget() const;
+
+ void setFrameRect(const QRect &);
+
+Q_SIGNALS:
+ void aboutToShow(int);
+ void aboutToShow(QWidget *);
+
+public Q_SLOTS:
+ void raiseWidget(int);
+ void raiseWidget(QWidget *);
+
+protected:
+ void frameChanged();
+ void resizeEvent(QResizeEvent *);
+ bool event(QEvent* e);
+
+ virtual void setChildGeometries();
+ void childEvent(QChildEvent *);
+
+private:
+ void init();
+
+ Q3WidgetStackPrivate * d;
+ Q3IntDict<QWidget> * dict;
+ Q3PtrDict<QWidget> * focusWidgets;
+ QWidget * topWidget;
+ QWidget * invisible;
+
+ Q_DISABLE_COPY(Q3WidgetStack)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3WIDGETSTACK_H
diff --git a/src/qt3support/widgets/widgets.pri b/src/qt3support/widgets/widgets.pri
new file mode 100644
index 0000000..9bd6fb4
--- /dev/null
+++ b/src/qt3support/widgets/widgets.pri
@@ -0,0 +1,58 @@
+# Qt compat module
+
+HEADERS += \
+ widgets/q3action.h \
+ widgets/q3buttongroup.h \
+ widgets/q3datetimeedit.h \
+ widgets/q3dockarea.h \
+ widgets/q3dockwindow.h \
+ widgets/q3frame.h \
+ widgets/q3vbox.h \
+ widgets/q3vgroupbox.h \
+ widgets/q3hbox.h \
+ widgets/q3hgroupbox.h \
+ widgets/q3grid.h \
+ widgets/q3gridview.h \
+ widgets/q3groupbox.h \
+ widgets/q3header.h \
+ widgets/q3mainwindow.h \
+ widgets/q3mainwindow_p.h \
+ widgets/q3progressbar.h \
+ widgets/q3scrollview.h \
+ widgets/q3titlebar_p.h \
+ widgets/q3toolbar.h \
+ widgets/q3whatsthis.h \
+ widgets/q3widgetstack.h \
+ widgets/q3button.h \
+ widgets/q3rangecontrol.h \
+ widgets/q3popupmenu.h \
+ widgets/q3combobox.h
+
+SOURCES += \
+ widgets/q3action.cpp \
+ widgets/q3buttongroup.cpp \
+ widgets/q3datetimeedit.cpp \
+ widgets/q3dockarea.cpp \
+ widgets/q3dockwindow.cpp \
+ widgets/q3frame.cpp \
+ widgets/q3vbox.cpp \
+ widgets/q3vgroupbox.cpp \
+ widgets/q3hbox.cpp \
+ widgets/q3hgroupbox.cpp \
+ widgets/q3grid.cpp \
+ widgets/q3gridview.cpp \
+ widgets/q3groupbox.cpp \
+ widgets/q3header.cpp \
+ widgets/q3mainwindow.cpp \
+ widgets/q3progressbar.cpp \
+ widgets/q3scrollview.cpp \
+ widgets/q3titlebar.cpp \
+ widgets/q3toolbar.cpp \
+ widgets/q3whatsthis.cpp \
+ widgets/q3widgetstack.cpp \
+ widgets/q3button.cpp \
+ widgets/q3rangecontrol.cpp \
+ widgets/q3spinwidget.cpp \
+ widgets/q3popupmenu.cpp \
+ widgets/q3combobox.cpp
+